ComposeActivity improvements (#548)
* do not add media urls to status text * add scrolling to content * add arrow icon and animation to replying-to toggle * remove unnecessary compose_button_colors.xml * improve toot button * improve bottom bar, add bottom sheet for compose options, dedicated cw button * fix crash on Android < API 21 * move media picking from dialog to bottom sheet * add small style tootbutton * fix colors/button background for light theme * add icons to media chose bottom sheet * improve hide media button, delete unused styles * fix crash on dev build when taking photo * consolidate drawables * consolidate strings and ids, add tooltips to buttons * allow media only toots * change error message to show max size of upload correctly * fix button color * add emoji * code cleanup * Merge branch 'master' into compose_activity_refactoring # Conflicts: # app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java * fix hidden snackbar * improve hint text color * add SendTootService * fix timeline refreshing * toot saving and error handling for sendtootservice * restructure some code * convert EditTextTyped to Kotlin * fixed pick media button disabled color * force sensitive media when content warning is shown * add db cache for emojis & fix tests * reorder buttons to match mastodon web * add possibility to cancel sending of toot * correctly delete sent toots * refresh SavedTootActivity after toot was sent * remove unused resources * correct params for toot saving in SendTootService * consolidate strings * bugfix * remove unused resources * fix notifications on old android for SendTootService * fix crash
This commit is contained in:
parent
8a23f034f0
commit
27eefbf65a
79 changed files with 1815 additions and 1234 deletions
|
@ -87,7 +87,7 @@ dependencies {
|
|||
|
||||
testImplementation "org.robolectric:robolectric:3.8"
|
||||
testImplementation "org.mockito:mockito-inline:2.17.0"
|
||||
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
|
||||
androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.1', {
|
||||
exclude group: 'com.android.support', module: 'support-annotations'
|
||||
})
|
||||
|
||||
|
|
|
@ -111,6 +111,7 @@
|
|||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service android:name=".service.SendTootService" />
|
||||
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
|
|
|
@ -7,7 +7,6 @@ import android.support.design.widget.Snackbar;
|
|||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -29,7 +29,6 @@ import android.support.design.widget.TabLayout;
|
|||
import android.support.graphics.drawable.VectorDrawableCompat;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.util.Log;
|
||||
|
@ -43,7 +42,6 @@ import com.keylesspalace.tusky.entity.Account;
|
|||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||
import com.keylesspalace.tusky.network.MastodonApi;
|
||||
import com.keylesspalace.tusky.pager.TimelinePagerAdapter;
|
||||
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
||||
import com.keylesspalace.tusky.util.NotificationHelper;
|
||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
|
||||
|
@ -242,16 +240,6 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity,
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == COMPOSE_RESULT && resultCode == ComposeActivity.RESULT_OK) {
|
||||
Intent intent = new Intent(TimelineReceiver.Types.STATUS_COMPOSED);
|
||||
LocalBroadcastManager.getInstance(getApplicationContext())
|
||||
.sendBroadcast(intent);
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (drawer != null && drawer.isDrawerOpen()) {
|
||||
|
|
|
@ -15,27 +15,29 @@
|
|||
|
||||
package com.keylesspalace.tusky;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.widget.DividerItemDecoration;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.keylesspalace.tusky.adapter.SavedTootAdapter;
|
||||
import com.keylesspalace.tusky.db.TootDao;
|
||||
import com.keylesspalace.tusky.db.TootEntity;
|
||||
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
||||
import com.keylesspalace.tusky.util.SaveTootHelper;
|
||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
@ -43,11 +45,12 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
public class SavedTootActivity extends BaseActivity implements SavedTootAdapter.SavedTootAction {
|
||||
private static final String TAG = "SavedTootActivity"; // logging tag
|
||||
|
||||
// dao
|
||||
private static TootDao tootDao = TuskyApplication.getDB().tootDao();
|
||||
|
||||
private SaveTootHelper saveTootHelper;
|
||||
|
||||
// ui
|
||||
private SavedTootAdapter adapter;
|
||||
private TextView noContent;
|
||||
|
@ -55,9 +58,27 @@ public class SavedTootActivity extends BaseActivity implements SavedTootAdapter.
|
|||
private List<TootEntity> toots = new ArrayList<>();
|
||||
@Nullable private AsyncTask<?, ?, ?> asyncTask;
|
||||
|
||||
private BroadcastReceiver broadcastReceiver;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
saveTootHelper = new SaveTootHelper(tootDao, this);
|
||||
|
||||
broadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
fetchToots();
|
||||
}
|
||||
};
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(TimelineReceiver.Types.STATUS_COMPOSED);
|
||||
|
||||
LocalBroadcastManager.getInstance(this)
|
||||
.registerReceiver(broadcastReceiver, intentFilter);
|
||||
|
||||
setContentView(R.layout.activity_saved_toot);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
|
@ -96,6 +117,12 @@ public class SavedTootActivity extends BaseActivity implements SavedTootAdapter.
|
|||
if (asyncTask != null) asyncTask.cancel(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
|
@ -122,19 +149,9 @@ public class SavedTootActivity extends BaseActivity implements SavedTootAdapter.
|
|||
|
||||
@Override
|
||||
public void delete(int position, TootEntity item) {
|
||||
// Delete any media files associated with the status.
|
||||
ArrayList<String> uris = new Gson().fromJson(item.getUrls(),
|
||||
new TypeToken<ArrayList<String>>() {}.getType());
|
||||
if (uris != null) {
|
||||
for (String uriString : uris) {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
if (getContentResolver().delete(uri, null, null) == 0) {
|
||||
Log.e(TAG, String.format("Did not delete file %s.", uriString));
|
||||
}
|
||||
}
|
||||
}
|
||||
// update DB
|
||||
tootDao.delete(item.getUid());
|
||||
|
||||
saveTootHelper.deleteDraft(item);
|
||||
|
||||
toots.remove(position);
|
||||
// update adapter
|
||||
if (adapter != null) {
|
||||
|
|
|
@ -17,6 +17,7 @@ package com.keylesspalace.tusky;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.app.Service;
|
||||
import android.app.UiModeManager;
|
||||
import android.arch.persistence.room.Room;
|
||||
import android.content.Context;
|
||||
|
@ -39,10 +40,11 @@ import javax.inject.Inject;
|
|||
import dagger.android.AndroidInjector;
|
||||
import dagger.android.DispatchingAndroidInjector;
|
||||
import dagger.android.HasActivityInjector;
|
||||
import dagger.android.HasServiceInjector;
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public class TuskyApplication extends Application implements HasActivityInjector {
|
||||
public class TuskyApplication extends Application implements HasActivityInjector, HasServiceInjector {
|
||||
public static final String APP_THEME_DEFAULT = ThemeUtils.THEME_NIGHT;
|
||||
|
||||
private static AppDatabase db;
|
||||
|
@ -50,6 +52,8 @@ public class TuskyApplication extends Application implements HasActivityInjector
|
|||
@Inject
|
||||
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
|
||||
@Inject
|
||||
DispatchingAndroidInjector<Service> dispatchingServiceInjector;
|
||||
@Inject
|
||||
NotificationPullJobCreator notificationPullJobCreator;
|
||||
|
||||
public static AppDatabase getDB() {
|
||||
|
@ -75,7 +79,7 @@ public class TuskyApplication extends Application implements HasActivityInjector
|
|||
|
||||
db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "tuskyDB")
|
||||
.allowMainThreadQueries()
|
||||
.addMigrations(AppDatabase.MIGRATION_2_3, AppDatabase.MIGRATION_3_4, AppDatabase.MIGRATION_4_5)
|
||||
.addMigrations(AppDatabase.MIGRATION_2_3, AppDatabase.MIGRATION_3_4, AppDatabase.MIGRATION_4_5, AppDatabase.MIGRATION_5_6)
|
||||
.build();
|
||||
accountManager = new AccountManager(db);
|
||||
serviceLocator = new ServiceLocator() {
|
||||
|
@ -90,7 +94,7 @@ public class TuskyApplication extends Application implements HasActivityInjector
|
|||
}
|
||||
};
|
||||
|
||||
AppInjector.INSTANCE.init(this);
|
||||
initAppInjector();
|
||||
initPicasso();
|
||||
|
||||
JobManager.create(this).addJobCreator(notificationPullJobCreator);
|
||||
|
@ -100,6 +104,10 @@ public class TuskyApplication extends Application implements HasActivityInjector
|
|||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
||||
}
|
||||
|
||||
protected void initAppInjector() {
|
||||
AppInjector.INSTANCE.init(this);
|
||||
}
|
||||
|
||||
protected void initPicasso() {
|
||||
// Initialize Picasso configuration
|
||||
Picasso.Builder builder = new Picasso.Builder(this);
|
||||
|
@ -128,6 +136,11 @@ public class TuskyApplication extends Application implements HasActivityInjector
|
|||
return dispatchingAndroidInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidInjector<Service> serviceInjector() {
|
||||
return dispatchingServiceInjector;
|
||||
}
|
||||
|
||||
public interface ServiceLocator {
|
||||
<T> T get(Class<T> clazz);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/* Copyright 2018 Conny Duck
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.adapter
|
||||
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.entity.Emoji
|
||||
import com.squareup.picasso.Picasso
|
||||
|
||||
class EmojiAdapter(private val emojiList: List<Emoji>, private val onEmojiSelectedListener: OnEmojiSelectedListener) : RecyclerView.Adapter<EmojiAdapter.EmojiHolder>() {
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return emojiList.size
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmojiAdapter.EmojiHolder {
|
||||
|
||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_emoji_button, parent, false) as ImageView
|
||||
return EmojiHolder(view)
|
||||
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(viewHolder: EmojiAdapter.EmojiHolder, position: Int) {
|
||||
Picasso.with(viewHolder.emojiImageView.context)
|
||||
.load(emojiList[position].url)
|
||||
.into(viewHolder.emojiImageView)
|
||||
|
||||
viewHolder.emojiImageView.setOnClickListener {
|
||||
onEmojiSelectedListener.onEmojiSelected(emojiList[position].shortcode)
|
||||
}
|
||||
}
|
||||
|
||||
class EmojiHolder(val emojiImageView: ImageView) : RecyclerView.ViewHolder(emojiImageView)
|
||||
|
||||
}
|
||||
|
||||
interface OnEmojiSelectedListener {
|
||||
fun onEmojiSelected(shortcode: String)
|
||||
}
|
|
@ -37,8 +37,8 @@ import android.widget.TextView;
|
|||
import android.widget.ToggleButton;
|
||||
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Emoji;
|
||||
import com.keylesspalace.tusky.entity.Notification;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.interfaces.LinkListener;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
|
@ -485,7 +485,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
|||
}
|
||||
|
||||
Spanned content = statusViewData.getContent();
|
||||
List<Status.Emoji> emojis = statusViewData.getEmojis();
|
||||
List<Emoji> emojis = statusViewData.getEmojis();
|
||||
|
||||
Spanned emojifiedText = CustomEmojiHelper.emojifyText(content, emojis, statusContent);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import android.widget.ToggleButton;
|
|||
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Attachment;
|
||||
import com.keylesspalace.tusky.entity.Emoji;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
|
@ -106,7 +107,7 @@ abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
username.setText(usernameText);
|
||||
}
|
||||
|
||||
private void setContent(Spanned content, Status.Mention[] mentions, List<Status.Emoji> emojis,
|
||||
private void setContent(Spanned content, Status.Mention[] mentions, List<Emoji> emojis,
|
||||
StatusActionListener listener) {
|
||||
Spanned emojifiedText = CustomEmojiHelper.emojifyText(content, emojis, this.content);
|
||||
|
||||
|
@ -384,7 +385,7 @@ abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
sensitiveMediaShow.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void setSpoilerText(String spoilerText, List<Status.Emoji> emojis,
|
||||
private void setSpoilerText(String spoilerText, List<Emoji> emojis,
|
||||
final boolean expanded, final StatusActionListener listener) {
|
||||
CharSequence emojiSpoiler =
|
||||
CustomEmojiHelper.emojifyString(spoilerText, emojis, contentWarningDescription);
|
||||
|
|
|
@ -15,9 +15,7 @@
|
|||
|
||||
package com.keylesspalace.tusky.db
|
||||
|
||||
import android.arch.persistence.room.Database
|
||||
import android.util.Log
|
||||
import com.keylesspalace.tusky.TuskyApplication
|
||||
import com.keylesspalace.tusky.entity.Account
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,11 +25,12 @@ import android.support.annotation.NonNull;
|
|||
* DB version & declare DAO
|
||||
*/
|
||||
|
||||
@Database(entities = {TootEntity.class, AccountEntity.class}, version = 5, exportSchema = false)
|
||||
@Database(entities = {TootEntity.class, AccountEntity.class, EmojiListEntity.class}, version = 6, exportSchema = false)
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
|
||||
public abstract TootDao tootDao();
|
||||
public abstract AccountDao accountDao();
|
||||
public abstract EmojiListDao emojiListDao();
|
||||
|
||||
public static final Migration MIGRATION_2_3 = new Migration(2, 3) {
|
||||
@Override
|
||||
|
@ -74,4 +75,11 @@ public abstract class AppDatabase extends RoomDatabase {
|
|||
database.execSQL("CREATE UNIQUE INDEX `index_AccountEntity_domain_accountId` ON `AccountEntity` (`domain`, `accountId`)");
|
||||
}
|
||||
};
|
||||
|
||||
public static final Migration MIGRATION_5_6 = new Migration(5, 6) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase database) {
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS `EmojiListEntity` (`instance` TEXT NOT NULL, `emojiList` TEXT NOT NULL, PRIMARY KEY(`instance`))");
|
||||
}
|
||||
};
|
||||
}
|
31
app/src/main/java/com/keylesspalace/tusky/db/EmojiListDao.kt
Normal file
31
app/src/main/java/com/keylesspalace/tusky/db/EmojiListDao.kt
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* Copyright 2018 Conny Duck
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.db
|
||||
|
||||
import android.arch.persistence.room.Dao
|
||||
import android.arch.persistence.room.Insert
|
||||
import android.arch.persistence.room.OnConflictStrategy
|
||||
import android.arch.persistence.room.Query
|
||||
|
||||
@Dao
|
||||
interface EmojiListDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertOrReplace(emojiList: EmojiListEntity)
|
||||
|
||||
@Query("SELECT * FROM EmojiListEntity WHERE instance = :instance LIMIT 1")
|
||||
fun loadEmojisForInstance(instance: String): EmojiListEntity?
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/* Copyright 2018 Conny Duck
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.db
|
||||
|
||||
import android.arch.persistence.room.Entity
|
||||
import android.arch.persistence.room.PrimaryKey
|
||||
import android.arch.persistence.room.TypeConverter
|
||||
import android.arch.persistence.room.TypeConverters
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.keylesspalace.tusky.entity.Emoji
|
||||
|
||||
@Entity
|
||||
@TypeConverters(Converters::class)
|
||||
data class EmojiListEntity(@field:PrimaryKey var instance: String,
|
||||
val emojiList: List<Emoji>)
|
||||
|
||||
|
||||
class Converters {
|
||||
|
||||
@TypeConverter
|
||||
fun jsonToList(emojiListJson: String): List<Emoji> {
|
||||
return Gson().fromJson(emojiListJson, object : TypeToken<List<Emoji>>() {}.type)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun listToJson(emojiList: List<Emoji>): String {
|
||||
return Gson().toJson(emojiList)
|
||||
}
|
||||
}
|
|
@ -38,4 +38,7 @@ public interface TootDao {
|
|||
|
||||
@Query("DELETE FROM TootEntity WHERE uid = :uid")
|
||||
int delete(int uid);
|
||||
|
||||
@Query("SELECT * FROM TootEntity WHERE uid = :uid")
|
||||
TootEntity find(int uid);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,8 @@ import javax.inject.Singleton
|
|||
AppModule::class,
|
||||
NetworkModule::class,
|
||||
AndroidInjectionModule::class,
|
||||
ActivitiesModule::class
|
||||
ActivitiesModule::class,
|
||||
ServicesModule::class
|
||||
])
|
||||
interface AppComponent {
|
||||
@Component.Builder
|
||||
|
|
|
@ -21,7 +21,6 @@ import android.content.Context
|
|||
import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v4.content.LocalBroadcastManager
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.TuskyApplication
|
||||
import com.keylesspalace.tusky.db.AccountManager
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/* Copyright 2018 Conny Duck
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.di
|
||||
|
||||
import com.keylesspalace.tusky.service.SendTootService
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
|
||||
@Module
|
||||
abstract class ServicesModule {
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributeMyService(): SendTootService
|
||||
}
|
25
app/src/main/java/com/keylesspalace/tusky/entity/Emoji.kt
Normal file
25
app/src/main/java/com/keylesspalace/tusky/entity/Emoji.kt
Normal file
|
@ -0,0 +1,25 @@
|
|||
/* Copyright 2018 Conny Duck
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class Emoji(
|
||||
val shortcode: String,
|
||||
val url: String
|
||||
) : Parcelable
|
|
@ -131,11 +131,6 @@ data class Status(
|
|||
var website: String? = null
|
||||
}
|
||||
|
||||
class Emoji {
|
||||
val shortcode: String? = null
|
||||
val url: String? = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MAX_MEDIA_ATTACHMENTS = 4
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ import android.view.View;
|
|||
import android.view.ViewGroup;
|
||||
|
||||
import com.keylesspalace.tusky.AccountActivity;
|
||||
import com.keylesspalace.tusky.BaseActivity;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.adapter.AccountAdapter;
|
||||
import com.keylesspalace.tusky.adapter.BlocksAdapter;
|
||||
|
|
|
@ -1,161 +0,0 @@
|
|||
/* Copyright 2017 Andrew Dawson
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.fragment;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.BottomSheetDialogFragment;
|
||||
import android.support.graphics.drawable.VectorDrawableCompat;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.RadioGroup;
|
||||
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||
|
||||
public class ComposeOptionsFragment extends BottomSheetDialogFragment {
|
||||
public interface Listener {
|
||||
void onVisibilityChanged(Status.Visibility visibility);
|
||||
void onContentWarningChanged(boolean hideText);
|
||||
}
|
||||
|
||||
private RadioGroup radio;
|
||||
private CheckBox hideText;
|
||||
private Listener listener;
|
||||
|
||||
public static ComposeOptionsFragment newInstance(Status.Visibility visibility, boolean hideText) {
|
||||
Bundle arguments = new Bundle();
|
||||
ComposeOptionsFragment fragment = new ComposeOptionsFragment();
|
||||
arguments.putInt("visibilityNum", visibility.getNum());
|
||||
arguments.putBoolean("hideText", hideText);
|
||||
fragment.setArguments(arguments);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
listener = (Listener) context;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_compose_options, container, false);
|
||||
|
||||
Bundle arguments = getArguments();
|
||||
Status.Visibility visibility = Status.Visibility.byNum(
|
||||
arguments.getInt("visibilityNum", 0)
|
||||
);
|
||||
boolean statusHideText = arguments.getBoolean("hideText");
|
||||
|
||||
radio = rootView.findViewById(R.id.radio_visibility);
|
||||
int radioCheckedId = R.id.radio_public;
|
||||
switch (visibility) {
|
||||
case PUBLIC: radioCheckedId = R.id.radio_public; break;
|
||||
case PRIVATE: radioCheckedId = R.id.radio_private; break;
|
||||
case UNLISTED: radioCheckedId = R.id.radio_unlisted; break;
|
||||
case DIRECT: radioCheckedId = R.id.radio_direct; break;
|
||||
}
|
||||
radio.check(radioCheckedId);
|
||||
|
||||
RadioButton publicButton = rootView.findViewById(R.id.radio_public);
|
||||
RadioButton unlistedButton = rootView.findViewById(R.id.radio_unlisted);
|
||||
RadioButton privateButton = rootView.findViewById(R.id.radio_private);
|
||||
RadioButton directButton = rootView.findViewById(R.id.radio_direct);
|
||||
setRadioButtonDrawable(getContext(), publicButton, R.drawable.ic_public_24dp);
|
||||
setRadioButtonDrawable(getContext(), unlistedButton, R.drawable.ic_lock_open_24dp);
|
||||
setRadioButtonDrawable(getContext(), privateButton, R.drawable.ic_lock_outline_24dp);
|
||||
setRadioButtonDrawable(getContext(), directButton, R.drawable.ic_email_24dp);
|
||||
|
||||
hideText = rootView.findViewById(R.id.compose_hide_text);
|
||||
hideText.setChecked(statusHideText);
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
radio.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(RadioGroup group, int checkedId) {
|
||||
Status.Visibility visibility;
|
||||
switch (checkedId) {
|
||||
default:
|
||||
case R.id.radio_public: {
|
||||
visibility = Status.Visibility.PUBLIC;
|
||||
break;
|
||||
}
|
||||
case R.id.radio_unlisted: {
|
||||
visibility = Status.Visibility.UNLISTED;
|
||||
break;
|
||||
}
|
||||
case R.id.radio_private: {
|
||||
visibility = Status.Visibility.PRIVATE;
|
||||
break;
|
||||
}
|
||||
case R.id.radio_direct: {
|
||||
visibility = Status.Visibility.DIRECT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
listener.onVisibilityChanged(visibility);
|
||||
}
|
||||
});
|
||||
hideText.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
listener.onContentWarningChanged(isChecked);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void setRadioButtonDrawable(Context context, RadioButton button,
|
||||
@DrawableRes int id) {
|
||||
ColorStateList list = new ColorStateList(new int[][] {
|
||||
new int[] { -android.R.attr.state_checked },
|
||||
new int[] { android.R.attr.state_checked }
|
||||
}, new int[] {
|
||||
ThemeUtils.getColor(context, R.attr.compose_image_button_tint),
|
||||
ThemeUtils.getColor(context, R.attr.colorAccent)
|
||||
});
|
||||
Drawable drawable = VectorDrawableCompat.create(context.getResources(), id,
|
||||
context.getTheme());
|
||||
if (drawable == null) {
|
||||
return;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
button.setButtonTintList(list);
|
||||
} else {
|
||||
drawable = DrawableCompat.wrap(drawable);
|
||||
DrawableCompat.setTintList(drawable, list);
|
||||
}
|
||||
button.setButtonDrawable(drawable);
|
||||
}
|
||||
}
|
|
@ -20,18 +20,14 @@ import android.content.ClipboardManager;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.widget.PopupMenu;
|
||||
import android.text.Spanned;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import com.keylesspalace.tusky.AccountActivity;
|
||||
import com.keylesspalace.tusky.BaseActivity;
|
||||
import com.keylesspalace.tusky.ComposeActivity;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.ReportActivity;
|
||||
|
@ -41,27 +37,16 @@ import com.keylesspalace.tusky.ViewTagActivity;
|
|||
import com.keylesspalace.tusky.ViewThreadActivity;
|
||||
import com.keylesspalace.tusky.ViewVideoActivity;
|
||||
import com.keylesspalace.tusky.db.AccountEntity;
|
||||
import com.keylesspalace.tusky.di.Injectable;
|
||||
import com.keylesspalace.tusky.db.AccountManager;
|
||||
import com.keylesspalace.tusky.entity.Attachment;
|
||||
import com.keylesspalace.tusky.entity.Relationship;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.interfaces.AdapterItemRemover;
|
||||
import com.keylesspalace.tusky.network.MastodonApi;
|
||||
import com.keylesspalace.tusky.network.TimelineCases;
|
||||
import com.keylesspalace.tusky.receiver.TimelineReceiver;
|
||||
import com.keylesspalace.tusky.util.HtmlUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/* Note from Andrew on Jan. 22, 2017: This class is a design problem for me, so I left it with an
|
||||
* awkward name. TimelineFragment and NotificationFragment have significant overlap but the nature
|
||||
* of that is complicated by how they're coupled with Status and Notification and the corresponding
|
||||
|
|
|
@ -22,6 +22,7 @@ import com.keylesspalace.tusky.entity.Account;
|
|||
import com.keylesspalace.tusky.entity.AppCredentials;
|
||||
import com.keylesspalace.tusky.entity.Attachment;
|
||||
import com.keylesspalace.tusky.entity.Card;
|
||||
import com.keylesspalace.tusky.entity.Emoji;
|
||||
import com.keylesspalace.tusky.entity.MastoList;
|
||||
import com.keylesspalace.tusky.entity.Notification;
|
||||
import com.keylesspalace.tusky.entity.Relationship;
|
||||
|
@ -101,12 +102,15 @@ public interface MastodonApi {
|
|||
@FormUrlEncoded
|
||||
@POST("api/v1/statuses")
|
||||
Call<Status> createStatus(
|
||||
@Header("Authorization") String auth,
|
||||
@Header(DOMAIN_HEADER) String domain,
|
||||
@Field("status") String text,
|
||||
@Field("in_reply_to_id") String inReplyToId,
|
||||
@Field("spoiler_text") String warningText,
|
||||
@Field("visibility") String visibility,
|
||||
@Field("sensitive") Boolean sensitive,
|
||||
@Field("media_ids[]") List<String> mediaIds);
|
||||
@Field("media_ids[]") List<String> mediaIds,
|
||||
@Header("Idempotency-Key") String idempotencyKey);
|
||||
@GET("api/v1/statuses/{id}")
|
||||
Call<Status> status(@Path("id") String statusId);
|
||||
@GET("api/v1/statuses/{id}/context")
|
||||
|
@ -263,4 +267,7 @@ public interface MastodonApi {
|
|||
|
||||
@GET("/api/v1/lists")
|
||||
Call<List<MastoList>> getLists();
|
||||
|
||||
@GET("/api/v1/custom_emojis")
|
||||
Call<List<Emoji>> getCustomEmojis();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,329 @@
|
|||
package com.keylesspalace.tusky.service
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.Parcelable
|
||||
import android.support.v4.app.NotificationCompat
|
||||
import android.support.v4.app.ServiceCompat
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v4.content.LocalBroadcastManager
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.TuskyApplication
|
||||
import com.keylesspalace.tusky.db.AccountEntity
|
||||
import com.keylesspalace.tusky.db.AccountManager
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.receiver.TimelineReceiver
|
||||
import com.keylesspalace.tusky.util.SaveTootHelper
|
||||
import com.keylesspalace.tusky.util.StringUtils
|
||||
import dagger.android.AndroidInjection
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
|
||||
class SendTootService: Service(), Injectable {
|
||||
|
||||
@Inject
|
||||
lateinit var mastodonApi: MastodonApi
|
||||
@Inject
|
||||
lateinit var accountManager: AccountManager
|
||||
|
||||
private lateinit var saveTootHelper: SaveTootHelper
|
||||
|
||||
private val tootsToSend = ConcurrentHashMap<Int, TootToSend>()
|
||||
private val sendCalls = ConcurrentHashMap<Int, Call<Status>>()
|
||||
|
||||
private val timer = Timer()
|
||||
|
||||
|
||||
override fun onCreate() {
|
||||
AndroidInjection.inject(this)
|
||||
saveTootHelper = SaveTootHelper(TuskyApplication.getDB().tootDao(), this)
|
||||
super.onCreate()
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
|
||||
if(intent.hasExtra(KEY_TOOT)) {
|
||||
|
||||
val tootToSend = intent.getParcelableExtra<TootToSend>(KEY_TOOT)
|
||||
|
||||
if (tootToSend == null) {
|
||||
throw IllegalStateException("SendTootService started without $KEY_TOOT extra")
|
||||
}
|
||||
|
||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel(CHANNEL_ID, getString(R.string.send_toot_notification_channel_name), NotificationManager.IMPORTANCE_LOW)
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
|
||||
}
|
||||
|
||||
var notificationText = tootToSend.warningText
|
||||
if (notificationText.isBlank()) {
|
||||
notificationText = tootToSend.text
|
||||
}
|
||||
|
||||
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_notify)
|
||||
.setContentTitle(getString(R.string.send_toot_notification_title))
|
||||
.setContentText(notificationText)
|
||||
.setProgress(1, 0, true)
|
||||
.setOngoing(true)
|
||||
.setColor(ContextCompat.getColor(this, R.color.primary))
|
||||
.addAction(0, getString(android.R.string.cancel), cancelSendingIntent(notificationId))
|
||||
|
||||
if(tootsToSend.size == 0 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_DETACH)
|
||||
startForeground(notificationId, builder.build())
|
||||
} else {
|
||||
notificationManager.notify(notificationId, builder.build())
|
||||
}
|
||||
|
||||
tootsToSend[notificationId] = tootToSend
|
||||
sendToot(notificationId)
|
||||
|
||||
notificationId--
|
||||
|
||||
} else {
|
||||
|
||||
if(intent.hasExtra(KEY_CANCEL)) {
|
||||
cancelSending(intent.getIntExtra(KEY_CANCEL, 0))
|
||||
stopSelf(intent.getIntExtra(KEY_CANCEL, 0))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return START_NOT_STICKY
|
||||
|
||||
}
|
||||
|
||||
private fun sendToot(tootId: Int) {
|
||||
|
||||
// when tootToSend == null, sending has been canceled
|
||||
val tootToSend = tootsToSend[tootId] ?: return
|
||||
|
||||
// when account == null, user has logged out, cancel sending
|
||||
val account = accountManager.getAccountById(tootToSend.accountId)
|
||||
|
||||
if(account == null) {
|
||||
tootsToSend.remove(tootId)
|
||||
return
|
||||
}
|
||||
|
||||
tootToSend.retries++
|
||||
|
||||
val sendCall = mastodonApi.createStatus(
|
||||
"Bearer " + account.accessToken,
|
||||
account.domain,
|
||||
tootToSend.text,
|
||||
tootToSend.inReplyToId,
|
||||
tootToSend.warningText,
|
||||
tootToSend.visibility,
|
||||
tootToSend.sensitive,
|
||||
tootToSend.mediaIds,
|
||||
tootToSend.idempotencyKey
|
||||
)
|
||||
|
||||
|
||||
sendCalls[tootId] = sendCall
|
||||
|
||||
val callback = object: Callback<Status> {
|
||||
override fun onResponse(call: Call<Status>, response: Response<Status>) {
|
||||
|
||||
tootsToSend.remove(tootId)
|
||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
if (response.isSuccessful) {
|
||||
|
||||
val intent = Intent(TimelineReceiver.Types.STATUS_COMPOSED)
|
||||
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
|
||||
|
||||
// If the status was loaded from a draft, delete the draft and associated media files.
|
||||
if(tootToSend.savedTootUid != 0) {
|
||||
saveTootHelper.deleteDraft(tootToSend.savedTootUid)
|
||||
}
|
||||
|
||||
if (tootsToSend.isEmpty()) {
|
||||
ServiceCompat.stopForeground(this@SendTootService, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
notificationManager.cancel(tootId)
|
||||
|
||||
} else {
|
||||
// the server refused to accept the toot, save toot & show error message
|
||||
saveTootToDrafts(tootToSend)
|
||||
|
||||
val builder = NotificationCompat.Builder(this@SendTootService, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_notify)
|
||||
.setContentTitle(getString(R.string.send_toot_notification_error_title))
|
||||
.setContentText(getString(R.string.send_toot_notification_saved_content))
|
||||
.setColor(ContextCompat.getColor(this@SendTootService, R.color.primary))
|
||||
|
||||
notificationManager.notify(tootId, builder.build())
|
||||
|
||||
if (tootsToSend.isEmpty()) {
|
||||
ServiceCompat.stopForeground(this@SendTootService, ServiceCompat.STOP_FOREGROUND_DETACH)
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Status>, t: Throwable) {
|
||||
var backoff = 1000L*tootToSend.retries
|
||||
if (backoff > MAX_RETRY_INTERVAL) {
|
||||
backoff = MAX_RETRY_INTERVAL
|
||||
}
|
||||
|
||||
timer.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
sendToot(tootId)
|
||||
}
|
||||
}, backoff)
|
||||
}
|
||||
}
|
||||
|
||||
sendCall.enqueue(callback)
|
||||
|
||||
}
|
||||
|
||||
private fun cancelSending(tootId: Int) {
|
||||
val tootToCancel = tootsToSend.remove(tootId)
|
||||
if(tootToCancel != null) {
|
||||
val sendCall = sendCalls.remove(tootId)
|
||||
sendCall?.cancel()
|
||||
|
||||
saveTootToDrafts(tootToCancel)
|
||||
|
||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
val builder = NotificationCompat.Builder(this@SendTootService, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_notify)
|
||||
.setContentTitle(getString(R.string.send_toot_notification_cancel_title))
|
||||
.setContentText(getString(R.string.send_toot_notification_saved_content))
|
||||
.setColor(ContextCompat.getColor(this@SendTootService, R.color.primary))
|
||||
|
||||
notificationManager.notify(tootId, builder.build())
|
||||
|
||||
timer.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
notificationManager.cancel(tootId)
|
||||
}
|
||||
}, 5000)
|
||||
|
||||
if (tootsToSend.isEmpty()) {
|
||||
ServiceCompat.stopForeground(this@SendTootService, ServiceCompat.STOP_FOREGROUND_DETACH)
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveTootToDrafts(toot: TootToSend) {
|
||||
|
||||
saveTootHelper.saveToot(toot.text,
|
||||
toot.warningText,
|
||||
toot.savedJsonUrls,
|
||||
toot.mediaUris,
|
||||
toot.savedTootUid,
|
||||
toot.inReplyToId,
|
||||
toot.replyingStatusContent,
|
||||
toot.replyingStatusAuthorUsername,
|
||||
Status.Visibility.byString(toot.visibility))
|
||||
}
|
||||
|
||||
private fun cancelSendingIntent(tootId: Int): PendingIntent {
|
||||
|
||||
val intent = Intent(this, SendTootService::class.java)
|
||||
|
||||
intent.putExtra(KEY_CANCEL, tootId)
|
||||
|
||||
return PendingIntent.getService(this, tootId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
private const val KEY_TOOT = "toot"
|
||||
private const val KEY_CANCEL = "cancel_id"
|
||||
private const val CHANNEL_ID = "send_toots"
|
||||
|
||||
private const val MAX_RETRY_INTERVAL = 60*1000L // 1 minute
|
||||
|
||||
private var notificationId = -1 // use negative ids to not clash with other notis
|
||||
|
||||
@JvmStatic
|
||||
fun sendTootIntent(context: Context,
|
||||
text: String,
|
||||
warningText: String,
|
||||
visibility: Status.Visibility,
|
||||
sensitive: Boolean,
|
||||
mediaIds: List<String>,
|
||||
mediaUris: List<String>,
|
||||
inReplyToId: String?,
|
||||
replyingStatusContent: String?,
|
||||
replyingStatusAuthorUsername: String?,
|
||||
savedJsonUrls: String?,
|
||||
account: AccountEntity,
|
||||
savedTootUid: Int
|
||||
): Intent {
|
||||
val intent = Intent(context, SendTootService::class.java)
|
||||
|
||||
val idempotencyKey = StringUtils.randomAlphanumericString(16)
|
||||
|
||||
val tootToSend = TootToSend(text,
|
||||
warningText,
|
||||
visibility.serverString(),
|
||||
sensitive,
|
||||
mediaIds,
|
||||
mediaUris,
|
||||
inReplyToId,
|
||||
replyingStatusContent,
|
||||
replyingStatusAuthorUsername,
|
||||
savedJsonUrls,
|
||||
account.id,
|
||||
savedTootUid,
|
||||
idempotencyKey,
|
||||
0)
|
||||
|
||||
intent.putExtra(KEY_TOOT, tootToSend)
|
||||
|
||||
return intent
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class TootToSend(val text: String,
|
||||
val warningText: String,
|
||||
val visibility: String,
|
||||
val sensitive: Boolean,
|
||||
val mediaIds: List<String>,
|
||||
val mediaUris: List<String>,
|
||||
val inReplyToId: String?,
|
||||
val replyingStatusContent: String?,
|
||||
val replyingStatusAuthorUsername: String?,
|
||||
val savedJsonUrls: String?,
|
||||
val accountId: Long,
|
||||
val savedTootUid: Int,
|
||||
val idempotencyKey: String,
|
||||
var retries: Int): Parcelable
|
|
@ -28,7 +28,7 @@ import android.text.SpannedString;
|
|||
import android.text.style.ReplacementSpan;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.entity.Emoji;
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.squareup.picasso.Target;
|
||||
|
||||
|
@ -46,12 +46,12 @@ public class CustomEmojiHelper {
|
|||
* @param textView a reference to the textView the emojis will be shown in
|
||||
* @return the text with the shortcodes replaced by EmojiSpans
|
||||
*/
|
||||
public static Spanned emojifyText(Spanned text, List<Status.Emoji> emojis, final TextView textView) {
|
||||
public static Spanned emojifyText(Spanned text, List<Emoji> emojis, final TextView textView) {
|
||||
|
||||
if (!emojis.isEmpty()) {
|
||||
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder(text);
|
||||
for (Status.Emoji emoji : emojis) {
|
||||
for (Emoji emoji : emojis) {
|
||||
CharSequence pattern = new StringBuilder(":").append(emoji.getShortcode()).append(':');
|
||||
Matcher matcher = Pattern.compile(pattern.toString()).matcher(text);
|
||||
while (matcher.find()) {
|
||||
|
@ -71,7 +71,7 @@ public class CustomEmojiHelper {
|
|||
return text;
|
||||
}
|
||||
|
||||
public static Spanned emojifyString(String string, List<Status.Emoji> emojis, final TextView textView) {
|
||||
public static Spanned emojifyString(String string, List<Emoji> emojis, final TextView textView) {
|
||||
return emojifyText(new SpannedString(string), emojis, textView);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,14 +15,22 @@
|
|||
|
||||
package com.keylesspalace.tusky.util;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class IOUtils {
|
||||
public static void closeQuietly(@Nullable InputStream stream) {
|
||||
|
||||
private static final int DEFAULT_BLOCKSIZE = 16384;
|
||||
|
||||
public static void closeQuietly(@Nullable Closeable stream) {
|
||||
try {
|
||||
if (stream != null) {
|
||||
stream.close();
|
||||
|
@ -32,13 +40,32 @@ public class IOUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static void closeQuietly(@Nullable OutputStream stream) {
|
||||
public static boolean copyToFile(ContentResolver contentResolver, Uri uri, File file) {
|
||||
InputStream from;
|
||||
FileOutputStream to;
|
||||
try {
|
||||
if (stream != null) {
|
||||
stream.close();
|
||||
from = contentResolver.openInputStream(uri);
|
||||
to = new FileOutputStream(file);
|
||||
} catch (FileNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
if (from == null) {
|
||||
return false;
|
||||
}
|
||||
byte[] chunk = new byte[DEFAULT_BLOCKSIZE];
|
||||
try {
|
||||
while (true) {
|
||||
int bytes = from.read(chunk, 0, chunk.length);
|
||||
if (bytes < 0) {
|
||||
break;
|
||||
}
|
||||
to.write(chunk, 0, bytes);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// intentionally unhandled
|
||||
return false;
|
||||
}
|
||||
closeQuietly(from);
|
||||
closeQuietly(to);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
package com.keylesspalace.tusky.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.FileProvider;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.keylesspalace.tusky.BuildConfig;
|
||||
import com.keylesspalace.tusky.db.TootDao;
|
||||
import com.keylesspalace.tusky.db.TootEntity;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class SaveTootHelper {
|
||||
|
||||
private static final String TAG = "SaveTootHelper";
|
||||
|
||||
private TootDao tootDao;
|
||||
private Context context;
|
||||
|
||||
public SaveTootHelper(@NonNull TootDao tootDao, @NonNull Context context) {
|
||||
this.tootDao = tootDao;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public boolean saveToot(@NonNull String content,
|
||||
@NonNull String contentWarning,
|
||||
@Nullable String savedJsonUrls,
|
||||
@NonNull List<String> mediaUris,
|
||||
int savedTootUid,
|
||||
@Nullable String inReplyToId,
|
||||
@Nullable String replyingStatusContent,
|
||||
@Nullable String replyingStatusAuthorUsername,
|
||||
@NonNull Status.Visibility statusVisibility) {
|
||||
|
||||
if (TextUtils.isEmpty(content) && mediaUris.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get any existing file's URIs.
|
||||
ArrayList<String> existingUris = null;
|
||||
if (!TextUtils.isEmpty(savedJsonUrls)) {
|
||||
existingUris = new Gson().fromJson(savedJsonUrls,
|
||||
new TypeToken<ArrayList<String>>() {
|
||||
}.getType());
|
||||
}
|
||||
|
||||
String mediaUrlsSerialized = null;
|
||||
if (!ListUtils.isEmpty(mediaUris)) {
|
||||
List<String> savedList = saveMedia(mediaUris, existingUris);
|
||||
if (!ListUtils.isEmpty(savedList)) {
|
||||
mediaUrlsSerialized = new Gson().toJson(savedList);
|
||||
if (!ListUtils.isEmpty(existingUris)) {
|
||||
deleteMedia(setDifference(existingUris, savedList));
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (!ListUtils.isEmpty(existingUris)) {
|
||||
/* If there were URIs in the previous draft, but they've now been removed, those files
|
||||
* can be deleted. */
|
||||
deleteMedia(existingUris);
|
||||
}
|
||||
final TootEntity toot = new TootEntity(savedTootUid, content, mediaUrlsSerialized, contentWarning,
|
||||
inReplyToId,
|
||||
replyingStatusContent,
|
||||
replyingStatusAuthorUsername,
|
||||
statusVisibility);
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
tootDao.insertOrReplace(toot);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void deleteDraft(int tootId) {
|
||||
TootEntity item = tootDao.find(tootId);
|
||||
if(item != null) {
|
||||
deleteDraft(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteDraft(@NonNull TootEntity item){
|
||||
// Delete any media files associated with the status.
|
||||
ArrayList<String> uris = new Gson().fromJson(item.getUrls(),
|
||||
new TypeToken<ArrayList<String>>() {}.getType());
|
||||
if (uris != null) {
|
||||
for (String uriString : uris) {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
if (context.getContentResolver().delete(uri, null, null) == 0) {
|
||||
Log.e(TAG, String.format("Did not delete file %s.", uriString));
|
||||
}
|
||||
}
|
||||
}
|
||||
// update DB
|
||||
tootDao.delete(item.getUid());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<String> saveMedia(@NonNull List<String> mediaUris,
|
||||
@Nullable List<String> existingUris) {
|
||||
|
||||
File directory = context.getExternalFilesDir("Tusky");
|
||||
|
||||
if (directory == null || !(directory.exists())) {
|
||||
Log.e(TAG, "Error obtaining directory to save media.");
|
||||
return null;
|
||||
}
|
||||
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
ArrayList<File> filesSoFar = new ArrayList<>();
|
||||
ArrayList<String> results = new ArrayList<>();
|
||||
for (String mediaUri : mediaUris) {
|
||||
/* If the media was already saved in a previous draft, there's no need to save another
|
||||
* copy, just add the existing URI to the results. */
|
||||
if (existingUris != null) {
|
||||
int index = existingUris.indexOf(mediaUri);
|
||||
if (index != -1) {
|
||||
results.add(mediaUri);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Otherwise, save the media.
|
||||
|
||||
Uri uri = Uri.parse(mediaUri);
|
||||
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
|
||||
|
||||
String mimeType = contentResolver.getType(uri);
|
||||
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||
String fileExtension = map.getExtensionFromMimeType(mimeType);
|
||||
String filename = String.format("Tusky_Draft_Media_%s.%s", timeStamp, fileExtension);
|
||||
File file = new File(directory, filename);
|
||||
filesSoFar.add(file);
|
||||
boolean copied = IOUtils.copyToFile(contentResolver, uri, file);
|
||||
if (!copied) {
|
||||
/* If any media files were created in prior iterations, delete those before
|
||||
* returning. */
|
||||
for (File earlierFile : filesSoFar) {
|
||||
boolean deleted = earlierFile.delete();
|
||||
if (!deleted) {
|
||||
Log.i(TAG, "Could not delete the file " + earlierFile.toString());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Uri resultUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID+".fileprovider", file);
|
||||
results.add(resultUri.toString());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private void deleteMedia(List<String> mediaUris) {
|
||||
for (String uriString : mediaUris) {
|
||||
Uri uri = Uri.parse(uriString);
|
||||
if (context.getContentResolver().delete(uri, null, null) == 0) {
|
||||
Log.e(TAG, String.format("Did not delete file %s.", uriString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A∖B={x∈A|x∉B}
|
||||
*
|
||||
* @return all elements of set A that are not in set B.
|
||||
*/
|
||||
private static List<String> setDifference(List<String> a, List<String> b) {
|
||||
List<String> c = new ArrayList<>();
|
||||
for (String s : a) {
|
||||
if (!b.contains(s)) {
|
||||
c.add(s);
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/* Copyright 2018 Conny Duck
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.view
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.widget.LinearLayout
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import kotlinx.android.synthetic.main.view_compose_options.view.*
|
||||
|
||||
|
||||
class ComposeOptionsView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
var listener: ComposeOptionsListener? = null
|
||||
|
||||
init {
|
||||
inflate(context, R.layout.view_compose_options, this)
|
||||
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
publicRadioButton.setButtonDrawable(R.drawable.ic_public_24dp)
|
||||
unlistedRadioButton.setButtonDrawable(R.drawable.ic_lock_open_24dp)
|
||||
privateRadioButton.setButtonDrawable(R.drawable.ic_lock_outline_24dp)
|
||||
directRadioButton.setButtonDrawable(R.drawable.ic_email_24dp)
|
||||
}
|
||||
|
||||
visibilityRadioGroup.setOnCheckedChangeListener({ _, checkedId ->
|
||||
val visibility = when (checkedId) {
|
||||
R.id.publicRadioButton ->
|
||||
Status.Visibility.PUBLIC
|
||||
R.id.unlistedRadioButton ->
|
||||
Status.Visibility.UNLISTED
|
||||
R.id.privateRadioButton ->
|
||||
Status.Visibility.PRIVATE
|
||||
R.id.directRadioButton ->
|
||||
Status.Visibility.DIRECT
|
||||
else ->
|
||||
Status.Visibility.PUBLIC
|
||||
}
|
||||
listener?.onVisibilityChanged(visibility)
|
||||
})
|
||||
}
|
||||
|
||||
fun setStatusVisibility(visibility: Status.Visibility) {
|
||||
val selectedButton = when (visibility) {
|
||||
Status.Visibility.PUBLIC ->
|
||||
R.id.publicRadioButton
|
||||
Status.Visibility.UNLISTED ->
|
||||
R.id.unlistedRadioButton
|
||||
Status.Visibility.PRIVATE ->
|
||||
R.id.privateRadioButton
|
||||
Status.Visibility.DIRECT ->
|
||||
R.id.directRadioButton
|
||||
else ->
|
||||
R.id.directRadioButton
|
||||
|
||||
}
|
||||
|
||||
visibilityRadioGroup.check(selectedButton)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface ComposeOptionsListener {
|
||||
fun onVisibilityChanged(visibility: Status.Visibility)
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/* Copyright 2017 Andrew Dawson
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v13.view.inputmethod.EditorInfoCompat;
|
||||
import android.support.v13.view.inputmethod.InputConnectionCompat;
|
||||
import android.support.v7.widget.AppCompatMultiAutoCompleteTextView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import com.keylesspalace.tusky.util.Assert;
|
||||
|
||||
public class EditTextTyped extends AppCompatMultiAutoCompleteTextView {
|
||||
|
||||
private InputConnectionCompat.OnCommitContentListener onCommitContentListener;
|
||||
private String[] mimeTypes;
|
||||
|
||||
public EditTextTyped(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public EditTextTyped(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
}
|
||||
|
||||
public void setMimeTypes(String[] types,
|
||||
InputConnectionCompat.OnCommitContentListener listener) {
|
||||
mimeTypes = types;
|
||||
onCommitContentListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
|
||||
InputConnection connection = super.onCreateInputConnection(editorInfo);
|
||||
if (onCommitContentListener != null) {
|
||||
Assert.expect(mimeTypes != null);
|
||||
EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes);
|
||||
return InputConnectionCompat.createWrapper(connection, editorInfo,
|
||||
onCommitContentListener);
|
||||
} else {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/* Copyright 2018 Conny Duck
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.view
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v13.view.inputmethod.EditorInfoCompat
|
||||
import android.support.v13.view.inputmethod.InputConnectionCompat
|
||||
import android.support.v7.widget.AppCompatMultiAutoCompleteTextView
|
||||
import android.text.InputType
|
||||
import android.util.AttributeSet
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputConnection
|
||||
|
||||
class EditTextTyped @JvmOverloads constructor(context: Context,
|
||||
attributeSet: AttributeSet? = null)
|
||||
: AppCompatMultiAutoCompleteTextView(context, attributeSet) {
|
||||
|
||||
private var onCommitContentListener: InputConnectionCompat.OnCommitContentListener? = null
|
||||
|
||||
init {
|
||||
//fix a bug with autocomplete and some keyboards
|
||||
val newInputType = inputType and (inputType xor InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE)
|
||||
inputType = newInputType
|
||||
}
|
||||
|
||||
fun setOnCommitContentListener(listener: InputConnectionCompat.OnCommitContentListener) {
|
||||
onCommitContentListener = listener
|
||||
}
|
||||
|
||||
override fun onCreateInputConnection(editorInfo: EditorInfo): InputConnection {
|
||||
val connection = super.onCreateInputConnection(editorInfo)
|
||||
return if (onCommitContentListener != null) {
|
||||
EditorInfoCompat.setContentMimeTypes(editorInfo, arrayOf("image/*"))
|
||||
InputConnectionCompat.createWrapper(connection, editorInfo,
|
||||
onCommitContentListener!!)
|
||||
} else {
|
||||
connection
|
||||
}
|
||||
}
|
||||
}
|
76
app/src/main/java/com/keylesspalace/tusky/view/TootButton.kt
Normal file
76
app/src/main/java/com/keylesspalace/tusky/view/TootButton.kt
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* Copyright 2018 Conny Duck
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
* Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
package com.keylesspalace.tusky.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.support.v7.widget.AppCompatButton
|
||||
import android.util.AttributeSet
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
|
||||
class TootButton
|
||||
@JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : AppCompatButton(context, attrs, defStyleAttr) {
|
||||
|
||||
private val smallStyle: Boolean = context.resources.getBoolean(R.bool.show_small_toot_button)
|
||||
|
||||
init {
|
||||
if(smallStyle) {
|
||||
setCompoundDrawablesRelativeWithIntrinsicBounds(R.drawable.ic_send_24dp, 0, 0, 0)
|
||||
} else {
|
||||
compoundDrawablePadding = context.resources.getDimensionPixelSize(R.dimen.toot_button_drawable_padding)
|
||||
setText(R.string.action_send)
|
||||
}
|
||||
}
|
||||
|
||||
fun setStatusVisibility(visibility: Status.Visibility) {
|
||||
if(!smallStyle) {
|
||||
|
||||
when (visibility) {
|
||||
Status.Visibility.PUBLIC -> {
|
||||
setText(R.string.action_send_public)
|
||||
setCompoundDrawables(null, null, null, null)
|
||||
}
|
||||
Status.Visibility.UNLISTED -> {
|
||||
setText(R.string.action_send)
|
||||
setCompoundDrawables(null, null, null, null)
|
||||
}
|
||||
Status.Visibility.PRIVATE,
|
||||
Status.Visibility.DIRECT -> {
|
||||
addLock()
|
||||
}
|
||||
else -> {
|
||||
setCompoundDrawables(null, null, null, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun addLock() {
|
||||
setText(R.string.action_send)
|
||||
val lock = IconicsDrawable(context, GoogleMaterial.Icon.gmd_lock).sizeDp(18).color(Color.WHITE)
|
||||
setCompoundDrawablesWithIntrinsicBounds(lock, null, null, null)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import android.text.Spanned;
|
|||
|
||||
import com.keylesspalace.tusky.entity.Attachment;
|
||||
import com.keylesspalace.tusky.entity.Card;
|
||||
import com.keylesspalace.tusky.entity.Emoji;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
|
||||
import java.util.Collections;
|
||||
|
@ -68,7 +69,7 @@ public abstract class StatusViewData {
|
|||
private final String senderId;
|
||||
private final boolean rebloggingEnabled;
|
||||
private final Status.Application application;
|
||||
private final List<Status.Emoji> emojis;
|
||||
private final List<Emoji> emojis;
|
||||
@Nullable
|
||||
private final Card card;
|
||||
|
||||
|
@ -78,7 +79,7 @@ public abstract class StatusViewData {
|
|||
boolean isShowingContent, String userFullName, String nickname, String avatar,
|
||||
Date createdAt, int reblogsCount, int favouritesCount, @Nullable String inReplyToId,
|
||||
@Nullable Status.Mention[] mentions, String senderId, boolean rebloggingEnabled,
|
||||
Status.Application application, List<Status.Emoji> emojis, @Nullable Card card) {
|
||||
Status.Application application, List<Emoji> emojis, @Nullable Card card) {
|
||||
this.id = id;
|
||||
this.content = content;
|
||||
this.reblogged = reblogged;
|
||||
|
@ -203,7 +204,7 @@ public abstract class StatusViewData {
|
|||
return application;
|
||||
}
|
||||
|
||||
public List<Status.Emoji> getEmojis() {
|
||||
public List<Emoji> getEmojis() {
|
||||
return emojis;
|
||||
}
|
||||
|
||||
|
@ -250,7 +251,7 @@ public abstract class StatusViewData {
|
|||
private String senderId;
|
||||
private boolean rebloggingEnabled;
|
||||
private Status.Application application;
|
||||
private List<Status.Emoji> emojis;
|
||||
private List<Emoji> emojis;
|
||||
private Card card;
|
||||
|
||||
public Builder() {
|
||||
|
@ -399,7 +400,7 @@ public abstract class StatusViewData {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder setEmojis(List<Status.Emoji> emojis) {
|
||||
public Builder setEmojis(List<Emoji> emojis) {
|
||||
this.emojis = emojis;
|
||||
return this;
|
||||
}
|
||||
|
|
5
app/src/main/res/color/compound_button_color_dark.xml
Normal file
5
app/src/main/res/color/compound_button_color_dark.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_checked="false" android:color="@color/text_color_tertiary_dark"/>
|
||||
<item android:state_checked="true" android:color="@color/primary"/>
|
||||
</selector>
|
5
app/src/main/res/color/compound_button_color_light.xml
Normal file
5
app/src/main/res/color/compound_button_color_light.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_checked="false" android:color="@color/text_color_tertiary_light"/>
|
||||
<item android:state_checked="true" android:color="@color/primary"/>
|
||||
</selector>
|
|
@ -1,32 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_enabled="true">
|
||||
<inset
|
||||
android:insetBottom="6dp"
|
||||
android:insetLeft="2dp"
|
||||
android:insetRight="6dp"
|
||||
android:insetTop="2dp">
|
||||
<shape>
|
||||
<corners android:radius="3dp" />
|
||||
<solid android:color="@color/md_blue_600" />
|
||||
<padding android:bottom="4dp" android:left="8dp" android:right="8dp" android:top="4dp" />
|
||||
</shape>
|
||||
</inset>
|
||||
</item>
|
||||
|
||||
<item android:state_enabled="false">
|
||||
<inset
|
||||
android:insetBottom="6dp"
|
||||
android:insetLeft="2dp"
|
||||
android:insetRight="6dp"
|
||||
android:insetTop="2dp">
|
||||
<shape>
|
||||
<corners android:radius="3dp" />
|
||||
<solid android:color="@color/md_blue_grey_300" />
|
||||
<padding android:bottom="4dp" android:left="8dp" android:right="8dp" android:top="4dp" />
|
||||
</shape>
|
||||
</inset>
|
||||
</item>
|
||||
|
||||
</selector>
|
|
@ -4,6 +4,6 @@ android:height="24dp"
|
|||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="@color/status_favourite_button_dark"
|
||||
android:fillColor="@color/text_color_tertiary_dark"
|
||||
android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
|
||||
</vector>
|
||||
|
|
|
@ -4,7 +4,7 @@ android:height="24dp"
|
|||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="@color/status_favourite_button_light"
|
||||
android:fillColor="@color/text_color_tertiary_light"
|
||||
android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
|
||||
</vector>
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="@color/toolbar_icon_dark"
|
||||
android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z" />
|
||||
</vector>
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#fff" android:pathData="M4,4H20A2,2 0 0,1 22,6V18A2,2 0 0,1 20,20H4C2.89,20 2,19.1 2,18V6C2,4.89 2.89,4 4,4M12,11L20,6H4L12,11M4,18H20V8.37L12,13.36L4,8.37V18Z" />
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_emoji_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_emoji_24dp.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M12,17.5C14.33,17.5 16.3,16.04 17.11,14H6.89C7.69,16.04 9.67,17.5 12,17.5M8.5,11A1.5,1.5 0 0,0 10,9.5A1.5,1.5 0 0,0 8.5,8A1.5,1.5 0 0,0 7,9.5A1.5,1.5 0 0,0 8.5,11M15.5,11A1.5,1.5 0 0,0 17,9.5A1.5,1.5 0 0,0 15.5,8A1.5,1.5 0 0,0 14,9.5A1.5,1.5 0 0,0 15.5,11M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20M12,2C6.47,2 2,6.5 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
|
||||
</vector>
|
|
@ -1,9 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z" />
|
||||
</vector>
|
|
@ -1,7 +1,9 @@
|
|||
<vector android:height="24dp" android:viewportHeight="35.43307"
|
||||
android:viewportWidth="35.43307" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillAlpha="1" android:fillColor="#ffffff"
|
||||
android:pathData="M4.99,5.56L2.99,7.56L7.55,12.13A17.72,17.72 0,0 0,2.4 17.71A17.72,17.72 0,0 0,17.72 26.57A17.72,17.72 0,0 0,21.56 26.13L26.79,31.37L28.79,29.36L4.99,5.56zM17.72,8.86A17.72,17.72 0,0 0,12.95 9.53L15.26,11.84A6.38,6.38 0,0 1,17.72 11.34A6.38,6.38 0,0 1,24.09 17.72A6.38,6.38 0,0 1,23.6 20.18L27.21,23.78A17.72,17.72 0,0 0,33.04 17.72A17.72,17.72 0,0 0,17.72 8.86zM17.72,14.17A3.54,3.54 0,0 0,17.6 14.18L21.26,17.83A3.54,3.54 0,0 0,21.26 17.72A3.54,3.54 0,0 0,17.72 14.17zM11.55,16.13L14.41,18.99A3.54,3.54 0,0 0,16.45 21.03L19.31,23.88A6.38,6.38 0,0 1,17.72 24.09A6.38,6.38 0,0 1,11.34 17.72A6.38,6.38 0,0 1,11.55 16.13z"
|
||||
android:strokeAlpha="1" android:strokeColor="#00000000"
|
||||
android:strokeLineCap="square" android:strokeLineJoin="miter" android:strokeWidth="2.65748024"/>
|
||||
</vector>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#fff"
|
||||
android:pathData="M11.83,9L15,12.16C15,12.11 15,12.05 15,12A3,3 0 0,0 12,9C11.94,9 11.89,9 11.83,9M7.53,9.8L9.08,11.35C9.03,11.56 9,11.77 9,12A3,3 0 0,0 12,15C12.22,15 12.44,14.97 12.65,14.92L14.2,16.47C13.53,16.8 12.79,17 12,17A5,5 0 0,1 7,12C7,11.21 7.2,10.47 7.53,9.8M2,4.27L4.28,6.55L4.73,7C3.08,8.3 1.78,10 1,12C2.73,16.39 7,19.5 12,19.5C13.55,19.5 15.03,19.2 16.38,18.66L16.81,19.08L19.73,22L21,20.73L3.27,3M12,7A5,5 0 0,1 17,12C17,12.64 16.87,13.26 16.64,13.82L19.57,16.75C21.07,15.5 22.27,13.86 23,12C21.27,7.61 17,4.5 12,4.5C10.6,4.5 9.26,4.75 8,5.2L10.17,7.35C10.74,7.13 11.35,7 12,7Z" />
|
||||
</vector>
|
|
@ -1,9 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
</vector>
|
|
@ -4,6 +4,6 @@
|
|||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="@color/status_reblog_button_dark"
|
||||
android:fillColor="@color/text_color_tertiary_dark"
|
||||
android:pathData="M7,7h10v3l4,-4 -4,-4v3L5,5v6h2L7,7zM17,17L7,17v-3l-4,4 4,4v-3h12v-6h-2v4z"/>
|
||||
</vector>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="@color/status_reblog_button_light"
|
||||
android:fillColor="@color/text_color_tertiary_light"
|
||||
android:pathData="M7,7h10v3l4,-4 -4,-4v3L5,5v6h2L7,7zM17,17L7,17v-3l-4,4 4,4v-3h12v-6h-2v4z"/>
|
||||
</vector>
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="@color/text_color_primary_dark"
|
||||
android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
|
||||
</vector>
|
|
@ -6,115 +6,122 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/composeAvatar"
|
||||
android:layout_width="?attr/actionBarSize"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_gravity="end"
|
||||
android:padding="8dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
<!--content description will be set in code -->
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
||||
<android.support.v4.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/composeAvatar"
|
||||
android:layout_width="?attr/actionBarSize"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_gravity="right|end"
|
||||
android:padding="8dp"
|
||||
tools:ignore="ContentDescription" />
|
||||
<!--content description will be set in code -->
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/reply_tv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:textSize="?attr/status_text_small"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
tools:text="Reply to @username"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/reply_content_tv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:background="?attr/compose_reply_content_background"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="4dp"
|
||||
android:textSize="?attr/status_text_small"
|
||||
android:visibility="gone"
|
||||
tools:text="Post content which may be preeettyy long, so please, make sure there's enough room for everything, okay? Not kidding. I wish Eugen answered me more often, sigh."
|
||||
tools:visibility="visible" />
|
||||
android:layout_marginBottom="52dp"
|
||||
android:layout_marginTop="?attr/actionBarSize">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/compose_content_warning_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/field_content_warning"
|
||||
<TextView
|
||||
android:id="@+id/composeReplyView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:drawablePadding="6dp"
|
||||
android:textSize="?attr/status_text_small"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
tools:text="Reply to @username"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/composeReplyContentView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:ems="10"
|
||||
android:hint="@string/hint_content_warning"
|
||||
android:inputType="text|textCapSentences"
|
||||
android:maxLines="1"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:background="?attr/compose_reply_content_background"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
android:paddingTop="4dp"
|
||||
android:textSize="?attr/status_text_small"
|
||||
android:visibility="gone"
|
||||
tools:text="Post content which may be preeettyy long, so please, make sure there's enough room for everything, okay? Not kidding. I wish Eugen answered me more often, sigh."
|
||||
tools:visibility="visible" />
|
||||
|
||||
<View
|
||||
<LinearLayout
|
||||
android:id="@+id/composeContentWarningBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="?android:attr/listDivider" />
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
</LinearLayout>
|
||||
<EditText
|
||||
android:id="@+id/composeContentWarningField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:hint="@string/hint_content_warning"
|
||||
android:inputType="text|textCapSentences"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:maxLines="1"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:textColorHint="?android:attr/textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp">
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="?android:attr/listDivider" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.keylesspalace.tusky.view.EditTextTyped
|
||||
android:id="@+id/compose_edit_field"
|
||||
android:id="@+id/composeEditField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/transparent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
android:completionThreshold="2"
|
||||
android:dropDownWidth="wrap_content"
|
||||
android:ems="10"
|
||||
android:gravity="start|top"
|
||||
android:hint="@string/hint_compose"
|
||||
android:inputType="text|textMultiLine|textCapSentences"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:textColorHint="?android:attr/textColorTertiary"
|
||||
android:textSize="?attr/status_text_large" />
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true">
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/compose_media_preview_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp">
|
||||
|
||||
<!--This is filled at runtime with ImageView's for each preview in the upload queue.-->
|
||||
|
||||
|
@ -122,104 +129,162 @@
|
|||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/postProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_centerVertical="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingBottom="8dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="4dp">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/compose_photo_pick"
|
||||
style="?attr/image_button_style"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:contentDescription="@string/action_photo_pick"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingTop="4dp"
|
||||
app:srcCompat="@drawable/ic_attach_file_24dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action_toggle_visibility"
|
||||
style="?attr/image_button_style"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:contentDescription="@string/action_compose_options"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingTop="4dp"
|
||||
app:srcCompat="@drawable/ic_public_24dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/compose_save_draft"
|
||||
style="?attr/image_button_style"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:contentDescription="@string/action_save"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingTop="4dp"
|
||||
app:srcCompat="@drawable/ic_save_24dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action_hide_media"
|
||||
style="?attr/image_button_style"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:contentDescription="@string/action_hide_media"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingTop="4dp"
|
||||
android:visibility="gone"
|
||||
app:srcCompat="@drawable/ic_hide_media_24dp" />
|
||||
|
||||
<android.support.v4.widget.Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/characters_left"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/floating_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:background="@drawable/compose_button_colors"
|
||||
android:text="@string/action_send"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
</LinearLayout>
|
||||
</android.support.v4.widget.NestedScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/addMediaBottomSheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:colorBackground"
|
||||
android:elevation="12dp"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="52dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
app:behavior_hideable="true"
|
||||
app:behavior_peekHeight="0dp"
|
||||
app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/action_photo_take"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="8dp"
|
||||
android:padding="8dp"
|
||||
android:text="@string/action_photo_take"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/action_photo_pick"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="8dp"
|
||||
android:padding="8dp"
|
||||
android:text="@string/action_add_media"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
</LinearLayout>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/emojiView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:colorBackground"
|
||||
android:clipToPadding="false"
|
||||
android:elevation="12dp"
|
||||
android:orientation="horizontal"
|
||||
android:paddingBottom="60dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
app:behavior_hideable="true"
|
||||
app:behavior_peekHeight="0dp"
|
||||
app:layout_behavior="android.support.design.widget.BottomSheetBehavior" />
|
||||
|
||||
<com.keylesspalace.tusky.view.ComposeOptionsView
|
||||
android:id="@+id/composeOptionsBottomSheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:colorBackground"
|
||||
android:elevation="12dp"
|
||||
android:paddingBottom="52dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="8dp"
|
||||
app:behavior_hideable="true"
|
||||
app:behavior_peekHeight="0dp"
|
||||
app:layout_behavior="android.support.design.widget.BottomSheetBehavior" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="?android:colorBackground"
|
||||
android:elevation="12dp"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingBottom="4dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="4dp">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/composeAddMediaButton"
|
||||
style="?attr/image_button_style"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:contentDescription="@string/action_add_media"
|
||||
android:padding="4dp"
|
||||
android:tooltipText="@string/action_add_media"
|
||||
app:srcCompat="@drawable/ic_attach_file_24dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/composeToggleVisibilityButton"
|
||||
style="?attr/image_button_style"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:contentDescription="@string/action_toggle_visibility"
|
||||
android:padding="4dp"
|
||||
android:tooltipText="@string/action_toggle_visibility"
|
||||
tools:src="@drawable/ic_public_24dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/composeHideMediaButton"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_hide_media"
|
||||
android:padding="4dp"
|
||||
android:tooltipText="@string/action_hide_media"
|
||||
android:visibility="gone"
|
||||
tools:src="@drawable/ic_eye_24dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/composeContentWarningButton"
|
||||
style="?attr/image_button_style"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:contentDescription="@string/action_content_warning"
|
||||
android:padding="4dp"
|
||||
android:text="CW"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:tooltipText="@string/action_content_warning" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/composeEmojiButton"
|
||||
style="?attr/image_button_style"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:contentDescription="@string/action_emoji_keyboard"
|
||||
android:padding="4dp"
|
||||
android:tooltipText="@string/action_emoji_keyboard"
|
||||
app:srcCompat="@drawable/ic_emoji_24dp" />
|
||||
|
||||
<android.support.v4.widget.Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/composeCharactersLeftView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
<com.keylesspalace.tusky.view.TootButton
|
||||
android:id="@+id/composeTootButton"
|
||||
style="@style/Widget.AppCompat.Button.Colored"
|
||||
android:layout_width="@dimen/toot_button_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:padding="16dp"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<RadioGroup
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/radio_visibility"
|
||||
android:checkedButton="@+id/radio_public"
|
||||
android:layout_margin="@dimen/compose_options_margin">
|
||||
|
||||
<RadioButton
|
||||
android:text="@string/visibility_public"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/radio_public"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<RadioButton
|
||||
android:text="@string/visibility_unlisted"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/radio_unlisted"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<RadioButton
|
||||
android:text="@string/visibility_private"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/radio_private"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<RadioButton
|
||||
android:text="@string/visibility_direct"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/radio_direct"
|
||||
android:layout_marginTop="5dp"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="@dimen/compose_options_margin"
|
||||
android:id="@+id/compose_hide_text"
|
||||
android:text="@string/action_hide_text" />
|
||||
|
||||
</LinearLayout>
|
10
app/src/main/res/layout/item_emoji_button.xml
Normal file
10
app/src/main/res/layout/item_emoji_button.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ImageView xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/composeEmojiButton"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_margin="4dp"
|
||||
android:padding="4dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
tools:ignore="ContentDescription" />
|
|
@ -244,7 +244,7 @@
|
|||
android:visibility="gone"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/status_media_preview_container"
|
||||
app:layout_constraintTop_toTopOf="@+id/status_media_preview_container"
|
||||
app:srcCompat="@drawable/ic_remove_red_eye_black_24dp" />
|
||||
app:srcCompat="@drawable/ic_eye_24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_sensitive_media_warning"
|
||||
|
|
|
@ -264,7 +264,7 @@
|
|||
android:visibility="gone"
|
||||
app:layout_constraintLeft_toLeftOf="@+id/status_media_preview_container"
|
||||
app:layout_constraintTop_toTopOf="@+id/status_media_preview_container"
|
||||
app:srcCompat="@drawable/ic_remove_red_eye_black_24dp" />
|
||||
app:srcCompat="@drawable/ic_eye_24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_sensitive_media_warning"
|
||||
|
|
69
app/src/main/res/layout/view_compose_options.xml
Normal file
69
app/src/main/res/layout/view_compose_options.xml
Normal file
|
@ -0,0 +1,69 @@
|
|||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:parentTag="android.widget.LinearLayout">
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/visibilityRadioGroup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="@dimen/compose_options_margin"
|
||||
android:layout_marginLeft="@dimen/compose_options_margin"
|
||||
android:layout_marginRight="@dimen/compose_options_margin"
|
||||
android:layout_marginTop="6dp"
|
||||
android:checkedButton="@+id/radio_public"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.AppCompatRadioButton
|
||||
android:id="@+id/publicRadioButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_weight="1"
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingStart="10dp"
|
||||
android:text="@string/visibility_public"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
app:buttonTint="?attr/compound_button_color" />
|
||||
|
||||
<android.support.v7.widget.AppCompatRadioButton
|
||||
android:id="@+id/unlistedRadioButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_weight="1"
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingStart="10dp"
|
||||
android:text="@string/visibility_unlisted"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
app:buttonTint="?attr/compound_button_color" />
|
||||
|
||||
<android.support.v7.widget.AppCompatRadioButton
|
||||
android:id="@+id/privateRadioButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_weight="1"
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingStart="10dp"
|
||||
android:text="@string/visibility_private"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
app:buttonTint="?attr/compound_button_color" />
|
||||
|
||||
<android.support.v7.widget.AppCompatRadioButton
|
||||
android:id="@+id/directRadioButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_weight="1"
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingStart="10dp"
|
||||
android:text="@string/visibility_direct"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
app:buttonTint="?attr/compound_button_color" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
</merge>
|
|
@ -71,7 +71,6 @@
|
|||
<string name="action_send">تبويق</string>
|
||||
<string name="action_send_public">بوّق</string>
|
||||
<string name="action_retry">إعادة المحاولة</string>
|
||||
<string name="action_hide_text">اخفي النص وراء تحذير</string>
|
||||
<string name="action_close">إغلاق</string>
|
||||
<string name="action_view_profile">الملف الشخصي</string>
|
||||
<string name="action_view_preferences">التفضيلات</string>
|
||||
|
@ -81,14 +80,13 @@
|
|||
<string name="action_view_follow_requests">طلبات المتابعة</string>
|
||||
<string name="action_view_media">وسائط</string>
|
||||
<string name="action_open_in_web">إفتح في متصفح</string>
|
||||
<string name="action_photo_pick">إضافة وسائط</string>
|
||||
<string name="action_add_media">إضافة وسائط</string>
|
||||
<string name="action_photo_take">أخذ صورة</string>
|
||||
<string name="action_share">شارك</string>
|
||||
<string name="action_mute">أكتم</string>
|
||||
<string name="action_unmute">إلغاء الكتم</string>
|
||||
<string name="action_mention">أذكر</string>
|
||||
<string name="action_hide_media">إخفاء الوسائط</string>
|
||||
<string name="action_compose_options">خيارات</string>
|
||||
<string name="action_open_drawer">إفتح الدرج</string>
|
||||
<string name="action_save">إحفظ</string>
|
||||
<string name="action_edit_profile">تعديل الملف الشخصي</string>
|
||||
|
@ -105,7 +103,6 @@
|
|||
<string name="send_status_link_to">شارك رابط التبويق على ...</string>
|
||||
<string name="send_status_content_to">شارك التبويق على …</string>
|
||||
|
||||
<string name="confirmation_send">بَوِّق</string>
|
||||
<string name="confirmation_reported">تم الإرسال !</string>
|
||||
<string name="confirmation_unblocked">تم فك الحجب عن الحساب</string>
|
||||
<string name="confirmation_unmuted">تم فك الكتم عن الحساب</string>
|
||||
|
@ -131,7 +128,6 @@
|
|||
<string name="dialog_download_image">تنزيل</string>
|
||||
<string name="dialog_message_follow_request">طلب المتابعة معلق : في إنتظار الرد</string>
|
||||
<string name="dialog_unfollow_warning">هل تود إلغاء متابعة هذا الحساب ؟</string>
|
||||
<string name="dialog_reply_not_found">تعذرت عملية إرسال هذا المنشور. إنّ المنشور الذي تود الرد عليه غير متوفر. هل تود حذف نص الرد ؟</string>
|
||||
|
||||
<string name="visibility_public">عمومي : ينشر على الخيوط العمومية</string>
|
||||
<string name="visibility_unlisted">غير مدرج : لا يُعرَض على الخيوط العمومية</string>
|
||||
|
@ -244,7 +240,6 @@
|
|||
<string name="state_follow_requested">طلب متابعة</string>
|
||||
|
||||
<string name="no_content">ليس هناك محتوى</string>
|
||||
<string name="action_save_one_toot">تم حفظ التبويق !</string>
|
||||
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">في %dy</string>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">L\'autorització s\'ha denegat.</string>
|
||||
<string name="error_retrieving_oauth_token">L\'obtenció del testimoni d\'inici de sessió ha fallat.</string>
|
||||
<string name="error_compose_character_limit">L\'estat és massa llarg!</string>
|
||||
<string name="error_media_upload_size">El fitxer ha de ser inferior a 4MB.</string>
|
||||
<string name="error_media_upload_size">El fitxer ha de ser inferior a 8MB.</string>
|
||||
<string name="error_media_upload_type">Aquest tipus de fitxer no es pot pujar.</string>
|
||||
<string name="error_media_upload_opening">Aquest tipus de fitxer no es pot obrir.</string>
|
||||
<string name="error_media_upload_permission">Cal permís de lectura del mitjà.</string>
|
||||
|
@ -69,7 +69,6 @@
|
|||
<string name="action_send">TOOT</string>
|
||||
<string name="action_send_public">TOOT!</string>
|
||||
<string name="action_retry">Torna a intentar-ho</string>
|
||||
<string name="action_hide_text">Amaga el text amb un avís</string>
|
||||
<string name="action_close">Tanca</string>
|
||||
<string name="action_view_profile">Perfil</string>
|
||||
<string name="action_view_preferences">Preferències</string>
|
||||
|
@ -79,14 +78,13 @@
|
|||
<string name="action_view_follow_requests">Peticions de seguiment</string>
|
||||
<string name="action_view_media">Multimèdia</string>
|
||||
<string name="action_open_in_web">Obre al navegador</string>
|
||||
<string name="action_photo_pick">Afegeix multimètida</string>
|
||||
<string name="action_add_media">Afegeix multimètida</string>
|
||||
<string name="action_photo_take">Fes una foto</string>
|
||||
<string name="action_share">Comparteix</string>
|
||||
<string name="action_mute">Silencia</string>
|
||||
<string name="action_unmute">Deixa de silenciar</string>
|
||||
<string name="action_mention">Menciona</string>
|
||||
<string name="action_hide_media">Amaga el multimèdia</string>
|
||||
<string name="action_compose_options">Opcions</string>
|
||||
<string name="action_open_drawer">Open drawer</string>
|
||||
<string name="action_save">Desa</string>
|
||||
<string name="action_edit_profile">Edita el perfil</string>
|
||||
|
@ -103,7 +101,6 @@
|
|||
<string name="send_status_link_to">Comparteix l\'URL del toot a...</string>
|
||||
<string name="send_status_content_to">Comparteix el toot a...</string>
|
||||
|
||||
<string name="confirmation_send">Toot!</string>
|
||||
<string name="confirmation_reported">Enviat!</string>
|
||||
<string name="confirmation_unblocked">Usuari desblocat</string>
|
||||
<string name="confirmation_unmuted">Usuari sense silenciar</string>
|
||||
|
@ -137,7 +134,6 @@
|
|||
<string name="dialog_download_image">Baixa</string>
|
||||
<string name="dialog_message_follow_request">Follow request pending: awaiting their response</string>
|
||||
<string name="dialog_unfollow_warning">Vols deixar de seguir aquest compte?</string>
|
||||
<string name="dialog_reply_not_found">Couldn\'t post this status. The status you\'re replying to might not be available. Remove reply info?</string>
|
||||
|
||||
<string name="visibility_public">Pública: és visible a la cronologia pública</string>
|
||||
<string name="visibility_unlisted">Sense llistar: no és visible a les cronologies públiques</string>
|
||||
|
@ -236,7 +232,6 @@
|
|||
<string name="state_follow_requested">Follow requested</string>
|
||||
|
||||
<string name="no_content">no hi ha cap contingut</string>
|
||||
<string name="action_save_one_toot">S\'ha desat el toot!</string>
|
||||
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">en %d anys</string>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">Autorisierung fehlgeschlagen.</string>
|
||||
<string name="error_retrieving_oauth_token">Es konnte kein Login-Token abgerufen werden.</string>
|
||||
<string name="error_compose_character_limit">Der Beitrag ist zu lang!</string>
|
||||
<string name="error_media_upload_size">Die Datei muss kleiner als 4MB sein.</string>
|
||||
<string name="error_media_upload_size">Die Datei muss kleiner als 8MB sein.</string>
|
||||
<string name="error_media_upload_type">Dieser Dateityp darf nicht hochgeladen werden.</string>
|
||||
<string name="error_media_upload_opening">Die Datei konnte nicht geöffnet werden.</string>
|
||||
<string name="error_media_upload_permission">Eine Leseberechtigung wird für das Hochladen der Mediendatei benötigt.</string>
|
||||
|
@ -63,7 +63,6 @@
|
|||
<string name="action_send">TRÖT</string>
|
||||
<string name="action_send_public">TRÖT!</string>
|
||||
<string name="action_retry">Erneut versuchen</string>
|
||||
<string name="action_hide_text">Verstecke Text hinter Warnung</string>
|
||||
<string name="action_close">Schließen</string>
|
||||
<string name="action_view_profile">Profil</string>
|
||||
<string name="action_view_preferences">Einstellungen</string>
|
||||
|
@ -71,13 +70,12 @@
|
|||
<string name="action_view_blocks">Blockierte Accounts</string>
|
||||
<string name="action_view_media">Medien</string>
|
||||
<string name="action_open_in_web">Im Browser öffnen</string>
|
||||
<string name="action_photo_pick">Füge Medien hinzu</string>
|
||||
<string name="action_add_media">Füge Medien hinzu</string>
|
||||
<string name="action_photo_take">Foto machen</string>
|
||||
<string name="action_share">Teilen</string>
|
||||
<string name="action_mute">Stummschalten</string>
|
||||
<string name="action_unmute">Lautschalten</string>
|
||||
<string name="action_mention">Erwähnen</string>
|
||||
<string name="action_compose_options">Einstellungen</string>
|
||||
<string name="action_open_drawer">Drawer öffnen</string>
|
||||
<string name="action_search">Suche</string>
|
||||
<string name="action_access_saved_toot">Entwürfe</string>
|
||||
|
@ -87,7 +85,6 @@
|
|||
<string name="send_status_link_to">Beitragslink teilen</string>
|
||||
<string name="send_status_content_to">Beitragsinhalt teilen</string>
|
||||
|
||||
<string name="confirmation_send">Teilen!</string>
|
||||
<string name="confirmation_reported">Gesendet!</string>
|
||||
|
||||
<string name="hint_domain">Welche Instanz?</string>
|
||||
|
@ -199,7 +196,6 @@
|
|||
</string>
|
||||
<string name="about_tusky_account">Tuskys Profil</string>
|
||||
|
||||
<string name="action_save_one_toot">Beitrag gespeichert</string>
|
||||
<string name="search_no_results">Keine Ergebnisse</string>
|
||||
<string name="status_media_images">Bilder</string>
|
||||
<string name="status_media_video">Video</string>
|
||||
|
@ -232,7 +228,6 @@
|
|||
<string name="title_x_following"><b>%s</b> Folgt</string>
|
||||
<string name="title_x_statuses"><b>%s</b> Beiträge</string>
|
||||
<string name="load_more_placeholder_text">mehr laden</string>
|
||||
<string name="dialog_reply_not_found">Fehler beim Senden des Status. Der Status, auf den du antwortest, ist vielleicht nicht mehr verfügbar. Als normale Erwähnung weiter bearbeiten?</string>
|
||||
<string name="pref_default_post_privacy">Beitragssichtbarkeit</string>
|
||||
<string name="pref_publishing">Beiträge</string>
|
||||
<string name="status_media_hidden_title">Medien versteckt</string>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">Authentification refusée.</string>
|
||||
<string name="error_retrieving_oauth_token">Impossible de récupérer le jeton d’authentification.</string>
|
||||
<string name="error_compose_character_limit">Votre pouet est trop long !</string>
|
||||
<string name="error_media_upload_size">Le fichier doit peser moins de 4 Mo.</string>
|
||||
<string name="error_media_upload_size">Le fichier doit peser moins de 8 Mo.</string>
|
||||
<string name="error_media_upload_type">Ce type de fichier n’est pas accepté.</string>
|
||||
<string name="error_media_upload_opening">Le fichier ne peut pas être ouvert.</string>
|
||||
<string name="error_media_upload_permission">Permission requise pour lire ce média.</string>
|
||||
|
@ -66,7 +66,6 @@
|
|||
<string name="action_send">POUET</string>
|
||||
<string name="action_send_public">POUET !</string>
|
||||
<string name="action_retry">Réessayer</string>
|
||||
<string name="action_hide_text">Masquer le texte par une mise en garde.</string>
|
||||
<string name="action_close">Fermer</string>
|
||||
<string name="action_view_profile">Profil</string>
|
||||
<string name="action_view_preferences">Préférences</string>
|
||||
|
@ -74,14 +73,12 @@
|
|||
<string name="action_view_blocks">Utilisateurs bloqués</string>
|
||||
<string name="action_view_media">Média</string>
|
||||
<string name="action_open_in_web">Ouvrir dans votre navigateur</string>
|
||||
<string name="action_photo_pick">Ajouter un média</string>
|
||||
<string name="action_add_media">Ajouter un média</string>
|
||||
<string name="action_photo_take">Prendre une photo</string>
|
||||
<string name="action_share">Partager</string>
|
||||
<string name="action_mute">Rendre muet</string>
|
||||
<string name="action_unmute">Redonner la parole</string>
|
||||
<string name="action_mention">Mention</string>
|
||||
<!--<string name="action_mark_sensitive">Mark media sensitive</string>-->
|
||||
<string name="action_compose_options">Options</string>
|
||||
<string name="action_open_drawer">Ouvrir le menu</string>
|
||||
<string name="action_save">Sauvegarder</string>
|
||||
<string name="action_edit_profile">Modifier le profil</string>
|
||||
|
@ -98,7 +95,6 @@
|
|||
<string name="send_status_content_to">Partager le pouet avec…</string>
|
||||
<string name="download_image">Téléchargement de %1$s</string>
|
||||
|
||||
<string name="confirmation_send">Pouet !</string>
|
||||
<string name="confirmation_reported">Envoyé !</string>
|
||||
|
||||
<string name="hint_domain">Quelle instance ?</string>
|
||||
|
@ -244,9 +240,6 @@
|
|||
<string name="status_media_images">Images</string>
|
||||
<string name="status_media_video">Videos</string>
|
||||
<string name="no_content">aucun contenu</string>
|
||||
<string name="action_save_one_toot">Pouet enregistré!</string>
|
||||
|
||||
<string name="dialog_reply_not_found">Impssible de poster ce status. Le status auquel vous répondez peut ne plus être disponible. Supprimer la réponse?</string>
|
||||
|
||||
<string name="action_logout_confirm">Êtes vous certain de vouloir déconnecter le compte %1$s?</string>
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">Engedélyezés letiltva.</string>
|
||||
<string name="error_retrieving_oauth_token">Bejelentkezési token megszerzése sikertelen.</string>
|
||||
<string name="error_compose_character_limit">Túl hosszú a tülkölés!</string>
|
||||
<string name="error_media_upload_size">A fájl kisebb kell legyen mint 4MB.</string>
|
||||
<string name="error_media_upload_size">A fájl kisebb kell legyen mint 8MB.</string>
|
||||
<string name="error_media_upload_type">Fájl feltöltése sikertelen.</string>
|
||||
<string name="error_media_upload_opening">Fájl megnyitása sikertelen.</string>
|
||||
<string name="error_media_upload_permission">Média olvasási engedély szükséges.</string>
|
||||
|
@ -66,7 +66,6 @@
|
|||
<string name="action_send">TÜLK</string>
|
||||
<string name="action_send_public">TÜLK!</string>
|
||||
<string name="action_retry">Próbálja újra</string>
|
||||
<string name="action_hide_text">Szöveg figyelmeztetés mögé helyezése</string>
|
||||
<string name="action_close">Bezár</string>
|
||||
<string name="action_view_profile">Profil</string>
|
||||
<string name="action_view_preferences">Preferenciák</string>
|
||||
|
@ -76,14 +75,13 @@
|
|||
<string name="action_view_follow_requests">Követési kérések</string>
|
||||
<string name="action_view_media">Média</string>
|
||||
<string name="action_open_in_web">Megnyitás böngészőben</string>
|
||||
<string name="action_photo_pick">Média hozzácsatolása</string>
|
||||
<string name="action_add_media">Média hozzácsatolása</string>
|
||||
<string name="action_photo_take">Kép készítése</string>
|
||||
<string name="action_share">Megoszt</string>
|
||||
<string name="action_mute">Némítás</string>
|
||||
<string name="action_unmute">Kinémítás</string>
|
||||
<string name="action_mention">Megemlítés</string>
|
||||
<string name="action_hide_media">Média elrejtése</string>
|
||||
<string name="action_compose_options">Beállítások</string>
|
||||
<string name="action_open_drawer">Drawer megnyitása</string>
|
||||
<string name="action_save">Mentés</string>
|
||||
<string name="action_edit_profile">Profil szerkesztése</string>
|
||||
|
@ -100,7 +98,6 @@
|
|||
<string name="send_status_link_to">Tülk URL megosztása…</string>
|
||||
<string name="send_status_content_to">Tülk megosztása…</string>
|
||||
|
||||
<string name="confirmation_send">Tülk!</string>
|
||||
<string name="confirmation_reported">Elküldve!</string>
|
||||
<string name="confirmation_unblocked">Felhasználó deblokkolva</string>
|
||||
<string name="confirmation_unmuted">Felhasználó kinémítva</string>
|
||||
|
@ -201,7 +198,6 @@
|
|||
<string name="state_follow_requested">Követés kérelmezve</string>
|
||||
|
||||
<string name="no_content">nincs tartalom</string>
|
||||
<string name="action_save_one_toot">Tülk mentve!</string>
|
||||
|
||||
<string name="follows_you">Követ téged</string>
|
||||
<string name="pref_title_alway_show_sensitive_media">Mindig mutassa a NSFW tartalmat</string>
|
||||
|
|
|
@ -71,7 +71,6 @@
|
|||
<string name="action_send">トゥート</string>
|
||||
<string name="action_send_public">トゥート!</string>
|
||||
<string name="action_retry">再試行</string>
|
||||
<string name="action_hide_text">テキストを注意書きで隠す</string>
|
||||
<string name="action_close">閉じる</string>
|
||||
<string name="action_view_profile">プロフィール</string>
|
||||
<string name="action_view_preferences">設定</string>
|
||||
|
@ -81,14 +80,13 @@
|
|||
<string name="action_view_follow_requests">フォローリクエスト</string>
|
||||
<string name="action_view_media">メディア</string>
|
||||
<string name="action_open_in_web">ブラウザで開く</string>
|
||||
<string name="action_photo_pick">メディアを追加</string>
|
||||
<string name="action_add_media">メディアを追加</string>
|
||||
<string name="action_photo_take">写真を撮る</string>
|
||||
<string name="action_share">共有</string>
|
||||
<string name="action_mute">ミュート</string>
|
||||
<string name="action_unmute">ミュート解除</string>
|
||||
<string name="action_mention">返信</string>
|
||||
<string name="action_hide_media">メディアを隠す</string>
|
||||
<string name="action_compose_options">オプション</string>
|
||||
<string name="action_open_drawer">メニューを開く</string>
|
||||
<string name="action_save">保存</string>
|
||||
<string name="action_edit_profile">プロフィールを編集</string>
|
||||
|
@ -105,7 +103,6 @@
|
|||
<string name="send_status_link_to">トゥートのURLを共有…</string>
|
||||
<string name="send_status_content_to">トゥートを共有…</string>
|
||||
|
||||
<string name="confirmation_send">トゥート!</string>
|
||||
<string name="confirmation_reported">送信しました!</string>
|
||||
<string name="confirmation_unblocked">ブロックを解除しました</string>
|
||||
<string name="confirmation_unmuted">ミュートを解除しました</string>
|
||||
|
@ -252,7 +249,6 @@
|
|||
<string name="state_follow_requested">フォローリクエスト中</string>
|
||||
|
||||
<string name="no_content">下書きはありません</string>
|
||||
<string name="action_save_one_toot">下書きに保存しました!</string>
|
||||
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">%d年後</string>
|
||||
|
|
|
@ -43,13 +43,9 @@
|
|||
<item name="account_toolbar_icon_tint_collapsed">@color/account_toolbar_icon_collapsed_dark</item>
|
||||
<item name="account_toolbar_popup_theme">@style/AppTheme.Account.ToolbarPopupTheme.Dark</item>
|
||||
<item name="compose_close_button_tint">@color/toolbar_icon_dark</item>
|
||||
<item name="compose_media_button_tint">@color/compose_media_button_dark</item>
|
||||
<item name="compose_media_button_disabled_tint">@color/compose_media_button_disabled_dark</item>
|
||||
<item name="compose_mention_color">@color/color_accent_dark</item>
|
||||
<item name="compose_content_warning_bar_background">@drawable/border_background_dark</item>
|
||||
<item name="compose_hide_media_button_color">@color/image_button_dark</item>
|
||||
<item name="compose_hide_media_button_selected_color">@color/color_accent_dark</item>
|
||||
<item name="compose_image_button_tint">@color/image_button_dark</item>
|
||||
<item name="compose_reply_content_background">@color/compose_reply_content_background_dark</item>
|
||||
|
||||
<item name="report_status_background_color">@color/color_background_dark</item>
|
||||
|
@ -70,10 +66,13 @@
|
|||
|
||||
<item name="play_indicator_drawable">@drawable/ic_play_indicator_dark</item>
|
||||
|
||||
<item name="compound_button_color">@color/compound_button_color_dark</item>
|
||||
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.ImageButton.Dark" parent="@style/Widget.AppCompat.Button.Borderless.Colored">
|
||||
<item name="android:tint">@color/image_button_dark</item>
|
||||
<item name="android:tint">@color/text_color_tertiary_dark</item>
|
||||
<item name="android:background">?attr/selectableItemBackgroundBorderless</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.BottomSheetDialog.Dark" parent="@style/Theme.Design.BottomSheetDialog">
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">Autorisatie werd geweigerd.</string>
|
||||
<string name="error_retrieving_oauth_token">Verkrijgen van inlogsleutel is mislukt.</string>
|
||||
<string name="error_compose_character_limit">Tekst van deze toot is te lang!</string>
|
||||
<string name="error_media_upload_size">Bestand moet kleiner zijn dan 4MB.</string>
|
||||
<string name="error_media_upload_size">Bestand moet kleiner zijn dan 8MB.</string>
|
||||
<string name="error_media_upload_type">Bestandstype kan niet worden geüpload.</string>
|
||||
<string name="error_media_upload_opening">Bestand kan niet worden geopend.</string>
|
||||
<string name="error_media_upload_permission">Toestemming nodig om media te kunnen lezen.</string>
|
||||
|
@ -60,7 +60,6 @@
|
|||
<string name="action_send">Toot</string>
|
||||
<string name="action_send_public">TOOT!</string>
|
||||
<string name="action_retry">Opnieuw proberen</string>
|
||||
<string name="action_hide_text">Tekst achter waarschuwing verbergen</string>
|
||||
<string name="action_close">Sluiten</string>
|
||||
<string name="action_view_profile">Profiel</string>
|
||||
<string name="action_view_preferences">Voorkeuren</string>
|
||||
|
@ -70,14 +69,13 @@
|
|||
<string name="action_view_blocks">Geblokkeerde gebruikers</string>
|
||||
<string name="action_view_media">Media</string>
|
||||
<string name="action_open_in_web">Open in webbrowser</string>
|
||||
<string name="action_photo_pick">Foto of video toevoegen</string>
|
||||
<string name="action_add_media">Foto of video toevoegen</string>
|
||||
<string name="action_photo_take">Foto of video maken</string>
|
||||
<string name="action_share">Delen</string>
|
||||
<string name="action_mute">Negeren</string>
|
||||
<string name="action_unmute">Niet meer negeren</string>
|
||||
<string name="action_mention">Vermelden</string>
|
||||
<string name="action_hide_media">Media verbergen</string>
|
||||
<string name="action_compose_options">Opties</string>
|
||||
<string name="action_open_drawer">Menu openen</string>
|
||||
<string name="action_save">Save</string>
|
||||
<string name="action_edit_profile">Profiel bewerken</string>
|
||||
|
@ -90,7 +88,6 @@
|
|||
<string name="action_copy_link">Link kopiëren</string>
|
||||
<string name="send_status_link_to">Link van toot delen</string>
|
||||
<string name="send_status_content_to">Inhoud van toot delen</string>
|
||||
<string name="confirmation_send">Toot!</string>
|
||||
<string name="confirmation_reported">Verzenden!</string>
|
||||
<string name="confirmation_unblocked">Gebruiker is gedeblokkeerd</string>
|
||||
<string name="confirmation_unmuted">Gebruiker wordt niet meer genegeerd</string>
|
||||
|
@ -181,7 +178,6 @@
|
|||
<string name="status_media_video">Video</string>
|
||||
<string name="state_follow_requested">Volgverzoek verzonden</string>
|
||||
<string name="no_content">geen inhoud</string>
|
||||
<string name="action_save_one_toot">Toot opgeslagen!</string>
|
||||
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">over %dj</string>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<string name="error_authorization_denied">Odmówiono autoryzacji.</string>
|
||||
<string name="error_retrieving_oauth_token">Nie udało się uzyskać tokenu logowania.</string>
|
||||
<string name="error_compose_character_limit">Zbyt długi wpis!</string>
|
||||
<string name="error_media_upload_size">Plik może mieć maksymalnie 4MB.</string>
|
||||
<string name="error_media_upload_size">Plik może mieć maksymalnie 8MB.</string>
|
||||
<string name="error_media_upload_type">Ten format pliku nie może zostać wysłany.</string>
|
||||
<string name="error_media_upload_opening">Nie można otworzyć tego pliku.</string>
|
||||
<string name="error_media_upload_permission">Wymagane jest pozwolenie na dostęp do plików z urządzenia.</string>
|
||||
|
@ -79,7 +79,6 @@
|
|||
<string name="action_send">Wyślij</string>
|
||||
<string name="action_send_public">Wyślij!</string>
|
||||
<string name="action_retry">Spróbuj ponownie</string>
|
||||
<string name="action_hide_text">Ukryj tekst przed ostrzeżeniem</string>
|
||||
<string name="action_close">Zamknij</string>
|
||||
<string name="action_view_profile">Profil</string>
|
||||
<string name="action_view_preferences">Preferencje</string>
|
||||
|
@ -89,14 +88,13 @@
|
|||
<string name="action_view_follow_requests">Prośby o możliwość śledzenia</string>
|
||||
<string name="action_view_media">Treści multimedialne</string>
|
||||
<string name="action_open_in_web">Otwórz w przeglądarce</string>
|
||||
<string name="action_photo_pick">Dodaj treści multimedialne</string>
|
||||
<string name="action_add_media">Dodaj treści multimedialne</string>
|
||||
<string name="action_photo_take">Wykonaj zdjęcie</string>
|
||||
<string name="action_share">Udostępnij</string>
|
||||
<string name="action_mute">Wycisz</string>
|
||||
<string name="action_unmute">Cofnij wyciszenie</string>
|
||||
<string name="action_mention">Wspomnij</string>
|
||||
<string name="action_hide_media">Ukryj zawartość multimedialną</string>
|
||||
<string name="action_compose_options">Opcje</string>
|
||||
<string name="action_open_drawer">Otwórz szufladę</string>
|
||||
<string name="action_save">Zapisz</string>
|
||||
<string name="action_edit_profile">Edytuj profil</string>
|
||||
|
@ -111,7 +109,6 @@
|
|||
<string name="send_status_content_to">Udostępnij wpis do…</string>
|
||||
|
||||
|
||||
<string name="confirmation_send">Wyślij!</string>
|
||||
<string name="confirmation_reported">Wyślij!</string>
|
||||
<string name="confirmation_unblocked">Odblokowano użytkownika</string>
|
||||
<string name="confirmation_unmuted">Cofnięto wyciszenie użytkownika</string>
|
||||
|
@ -152,7 +149,6 @@
|
|||
<string name="dialog_download_image">Pobierz</string>
|
||||
<string name="dialog_message_follow_request">Oczekująca prośba o możliwość śledzenia: oczekiwanie na odpowiedź</string>
|
||||
<string name="dialog_unfollow_warning">Czy chcesz przestać śledzić to konto?</string>
|
||||
<string name="dialog_reply_not_found">Nie można opublikować wpisu. Wpis na który odpisujesz mógł zostać usunięty. Chcesz opublikować wpis normalnie?</string>
|
||||
|
||||
|
||||
<string name="visibility_public">Publiczne: Opublikuj na publicznych osiach czasu</string>
|
||||
|
@ -272,7 +268,6 @@
|
|||
|
||||
|
||||
<string name="no_content">brak zawartości</string>
|
||||
<string name="action_save_one_toot">Zapisano wpis!</string>
|
||||
|
||||
|
||||
<string name="abbreviated_in_years">w %dy</string>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">Autorização negada.</string>
|
||||
<string name="error_retrieving_oauth_token">Falha ao adquirir token de entrada.</string>
|
||||
<string name="error_compose_character_limit">A postagem é muito longa!</string>
|
||||
<string name="error_media_upload_size">O arquivo deve ser menor que 4MB.</string>
|
||||
<string name="error_media_upload_size">O arquivo deve ser menor que 8MB.</string>
|
||||
<string name="error_media_upload_type">Esse tipo de arquivo não pode ser enviado.</string>
|
||||
<string name="error_media_upload_opening">Esse arquvo não pode ser aberto.</string>
|
||||
<string name="error_media_upload_permission">Permissão para ler mídia é necessária.</string>
|
||||
|
@ -69,7 +69,6 @@
|
|||
<string name="action_send">TOOT</string>
|
||||
<string name="action_send_public">TOOT!</string>
|
||||
<string name="action_retry">Tentar novamente</string>
|
||||
<string name="action_hide_text">Esconder texto com aviso de conteúdo</string>
|
||||
<string name="action_close">Fechar</string>
|
||||
<string name="action_view_profile">Perfil</string>
|
||||
<string name="action_view_preferences">Preferências</string>
|
||||
|
@ -79,14 +78,13 @@
|
|||
<string name="action_view_follow_requests">Solicitações de seguidor</string>
|
||||
<string name="action_view_media">Mídia</string>
|
||||
<string name="action_open_in_web">Abrir no navegador</string>
|
||||
<string name="action_photo_pick">Adicionar mídia</string>
|
||||
<string name="action_add_media">Adicionar mídia</string>
|
||||
<string name="action_photo_take">Tirar foto</string>
|
||||
<string name="action_share">Compartilhar</string>
|
||||
<string name="action_mute">Silenciar</string>
|
||||
<string name="action_unmute">Retirar silêncio</string>
|
||||
<string name="action_mention">Mencionar</string>
|
||||
<string name="action_hide_media">Esconder mídia</string>
|
||||
<string name="action_compose_options">Opções</string>
|
||||
<string name="action_open_drawer">Abrir gaveta</string>
|
||||
<string name="action_save">Salvar</string>
|
||||
<string name="action_edit_profile">Editar perfil</string>
|
||||
|
@ -103,7 +101,6 @@
|
|||
<string name="send_status_link_to">Compartilhar URL do toot no…</string>
|
||||
<string name="send_status_content_to">Compartilhar toot no…</string>
|
||||
|
||||
<string name="confirmation_send">Toot!</string>
|
||||
<string name="confirmation_reported">Enviado!</string>
|
||||
<string name="confirmation_unblocked">Usuário desbloqueado</string>
|
||||
<string name="confirmation_unmuted">Silêncio retirado</string>
|
||||
|
@ -209,7 +206,6 @@
|
|||
<string name="state_follow_requested">Solicitação enviada</string>
|
||||
|
||||
<string name="no_content">sem conteúdo</string>
|
||||
<string name="action_save_one_toot">Toot salvo!</string>
|
||||
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">em %dy</string>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">Авторизация была отклонена.</string>
|
||||
<string name="error_retrieving_oauth_token">Не удалось получить токен авторизации.</string>
|
||||
<string name="error_compose_character_limit">Статус слишком длинный!</string>
|
||||
<string name="error_media_upload_size">Файл должен быть не больше 4 Мбайт.</string>
|
||||
<string name="error_media_upload_size">Файл должен быть не больше 8 Мбайт.</string>
|
||||
<string name="error_media_upload_type">Данный тип файла не может быть загружен.</string>
|
||||
<string name="error_media_upload_opening">Файл не может быть открыт.</string>
|
||||
<string name="error_media_upload_permission">Необходимо разрешение на чтение медиаконтента.</string>
|
||||
|
@ -71,7 +71,6 @@
|
|||
<string name="action_send">ТРУБИТЬ</string>
|
||||
<string name="action_send_public">ТРУБИТЬ!</string>
|
||||
<string name="action_retry">Повторить</string>
|
||||
<string name="action_hide_text">Скрыть текст за предупреждением</string>
|
||||
<string name="action_close">Закрыть</string>
|
||||
<string name="action_view_profile">Профиль</string>
|
||||
<string name="action_view_preferences">Настройки</string>
|
||||
|
@ -81,14 +80,13 @@
|
|||
<string name="action_view_follow_requests">Запросы на подписку</string>
|
||||
<string name="action_view_media">Медиаконтент</string>
|
||||
<string name="action_open_in_web">Открыть в браузере</string>
|
||||
<string name="action_photo_pick">Добавить медиаконтент</string>
|
||||
<string name="action_add_media">Добавить медиаконтент</string>
|
||||
<string name="action_photo_take">Сфотографировать</string>
|
||||
<string name="action_share">Поделиться</string>
|
||||
<string name="action_mute">Заглушить</string>
|
||||
<string name="action_unmute">Отменить глушение</string>
|
||||
<string name="action_mention">Упомянуть</string>
|
||||
<string name="action_hide_media">Скрыть медиаконтент</string>
|
||||
<string name="action_compose_options">Опции</string>
|
||||
<string name="action_open_drawer">Нарисовать</string>
|
||||
<string name="action_save">Сохранить</string>
|
||||
<string name="action_edit_profile">Редактировать профиль</string>
|
||||
|
@ -105,7 +103,6 @@
|
|||
<string name="send_status_link_to">Поделиться ссылкой статуса</string>
|
||||
<string name="send_status_content_to">Поделиться статусом</string>
|
||||
|
||||
<string name="confirmation_send">Трубить!</string>
|
||||
<string name="confirmation_reported">Отправить!</string>
|
||||
<string name="confirmation_unblocked">Пользователь разблокирован</string>
|
||||
<string name="confirmation_unmuted">Глушение снято</string>
|
||||
|
@ -132,7 +129,6 @@
|
|||
<string name="dialog_download_image">Скачать</string>
|
||||
<string name="dialog_message_follow_request">Статус запроса на подписку: ожидается ответ</string>
|
||||
<string name="dialog_unfollow_warning">Отписаться от этого аккаунта?</string>
|
||||
<string name="dialog_reply_not_found">Не удалось опубликовать статус. Статус, на который вы отвечаете, может быть недоступен. Убрать информацию об ответе?</string>
|
||||
|
||||
<string name="visibility_public">Публичный: Показать в публичных лентах</string>
|
||||
<string name="visibility_unlisted">Скрытый: Не показывать в лентах</string>
|
||||
|
@ -242,7 +238,6 @@
|
|||
<string name="state_follow_requested">Запрошенные подписки</string>
|
||||
|
||||
<string name="no_content">ничего нет</string>
|
||||
<string name="action_save_one_toot">Пост сохранён!</string>
|
||||
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">через %dг</string>
|
||||
|
|
8
app/src/main/res/values-sw360dp/toot_button.xml
Normal file
8
app/src/main/res/values-sw360dp/toot_button.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item name="wrap_content" format="integer" type="dimen">-2</item>
|
||||
<bool name="show_small_toot_button">false</bool>
|
||||
<dimen name="toot_button_width">@dimen/wrap_content</dimen>
|
||||
<dimen name="toot_button_drawable_padding">4dp</dimen>
|
||||
|
||||
</resources>
|
|
@ -70,7 +70,6 @@
|
|||
<string name="action_send">TOOT</string>
|
||||
<string name="action_send_public">TOOT!</string>
|
||||
<string name="action_retry">மீண்டும் முயற்சி</string>
|
||||
<string name="action_hide_text">எச்சரிக்கைக்கு பின்னால் உரையை மறை</string>
|
||||
<string name="action_close">மூடு</string>
|
||||
<string name="action_view_profile">சுயவிவரம்</string>
|
||||
<string name="action_view_preferences">முன்னுரிமைகள்</string>
|
||||
|
@ -80,14 +79,12 @@
|
|||
<string name="action_view_follow_requests">பின்பற்ற கோரிக்கை</string>
|
||||
<string name="action_view_media">ஊடகம்</string>
|
||||
<string name="action_open_in_web">உலாவியில் திற</string>
|
||||
<string name="action_photo_pick">ஊடகத்தை சேர்</string>
|
||||
<string name="action_photo_take">புகைப்படம் எடு</string>
|
||||
<string name="action_share">பகிர்</string>
|
||||
<string name="action_mute">ஒலி நீக்கு</string>
|
||||
<string name="action_unmute">ஒலிக்க செய்</string>
|
||||
<string name="action_mention">குறிப்பிடு</string>
|
||||
<string name="action_hide_media">மீடியாவை மறை</string>
|
||||
<string name="action_compose_options">விருப்பங்கள்</string>
|
||||
<string name="action_open_drawer">டிராயரைத் திற</string>
|
||||
<string name="action_save">சேமி</string>
|
||||
<string name="action_edit_profile">சுயவிவரத்தை திருத்து</string>
|
||||
|
@ -104,7 +101,6 @@
|
|||
<string name="send_status_link_to">Toot URL-யை பகிர…</string>
|
||||
<string name="send_status_content_to">Toot உள்ளடக்கத்தை பகிர…</string>
|
||||
|
||||
<string name="confirmation_send">சமர்பி</string>
|
||||
<string name="confirmation_reported">அனுப்பு!</string>
|
||||
<string name="confirmation_unblocked">பயனர் முடக்கம் நீக்கப்பட்டது</string>
|
||||
<string name="confirmation_unmuted">பயனர் ஒலிக்க செய்யபட்டது</string>
|
||||
|
@ -135,7 +131,6 @@
|
|||
<string name="dialog_download_image">பதிவிறக்க</string>
|
||||
<string name="dialog_message_follow_request">கோரிக்கை நிலுவையில் உள்ளது, அவர்களின் பதிலுக்காக காத்திருக்கிறது</string>
|
||||
<string name="dialog_unfollow_warning">இந்த கணக்கை பின்பற்ற வேண்டாமா?</string>
|
||||
<string name="dialog_reply_not_found">இந்த நிலையை பதிவு செய்ய இயலவில்லை. தாங்கள் பதிலளிப்பதற்கான நிலையும் கிடைக்கபெறவில்லை.ஆதலால் பதில் தகவலை நீக்கவா?</string>
|
||||
|
||||
<string name="visibility_public">அனைவருக்கும் காண்பி</string>
|
||||
<string name="visibility_unlisted">அனைவருக்கும் காட்டாதே</string>
|
||||
|
@ -246,7 +241,6 @@
|
|||
<string name="state_follow_requested">கோரிக்கையைப் பின்பற்றவும்</string>
|
||||
|
||||
<string name="no_content">எந்த உள்ளடக்கமும் இல்லை</string>
|
||||
<string name="action_save_one_toot">Toot சேமிக்கபட்டது!</string>
|
||||
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">%dஆ-முன்</string>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">Kimlik doğrulama reddedildi.</string>
|
||||
<string name="error_retrieving_oauth_token">Giriş jetonu alınamadı.</string>
|
||||
<string name="error_compose_character_limit">İleti fazlasıyla uzun!</string>
|
||||
<string name="error_media_upload_size">Dosya 4MB\'ten küçük olmalı.</string>
|
||||
<string name="error_media_upload_size">Dosya 8MB\'ten küçük olmalı.</string>
|
||||
<string name="error_media_upload_type">O biçim dosya yüklenmez.</string>
|
||||
<string name="error_media_upload_opening">O dosya açılamadı.</string>
|
||||
<string name="error_media_upload_permission">Medya okuma izni gerekiyor.</string>
|
||||
|
@ -66,7 +66,6 @@
|
|||
<string name="action_send">İLET</string>
|
||||
<string name="action_send_public">İLET!</string>
|
||||
<string name="action_retry">Tekrar dene</string>
|
||||
<string name="action_hide_text">İçerik uyarıyla gizlensin</string>
|
||||
<string name="action_close">Kapat</string>
|
||||
<string name="action_view_profile">Profil</string>
|
||||
<string name="action_view_preferences">Ayarlar</string>
|
||||
|
@ -75,14 +74,12 @@
|
|||
<string name="action_view_blocks">Engellenmiş kullanıcılar</string>
|
||||
<string name="action_view_media">Medya</string>
|
||||
<string name="action_open_in_web">Tarayıcıda aç</string>
|
||||
<string name="action_photo_pick">Medya ekle</string>
|
||||
<string name="action_add_media">Medya ekle</string>
|
||||
<string name="action_photo_take">Resim çek</string>
|
||||
<string name="action_share">Paylaş</string>
|
||||
<string name="action_mute">Sesize al</string>
|
||||
<string name="action_unmute">Sesizden kaldır</string>
|
||||
<string name="action_mention">Bahset</string>
|
||||
<!--<string name="action_mark_sensitive">Mark media sensitive</string>-->
|
||||
<string name="action_compose_options">Seçenekler</string>
|
||||
<string name="action_open_drawer">Çekmece aç</string>
|
||||
<string name="action_save">Kaydet</string>
|
||||
<string name="action_edit_profile">Profili düzelt</string>
|
||||
|
@ -93,7 +90,6 @@
|
|||
<string name="send_status_link_to">İletinin adresini paylaş…</string>
|
||||
<string name="send_status_content_to">İletiyi paylaş…</string>
|
||||
|
||||
<string name="confirmation_send">İlet!</string>
|
||||
<string name="confirmation_reported">İletildi!</string>
|
||||
|
||||
<string name="hint_domain">Hangi sunucu?</string>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">授权被拒绝。</string>
|
||||
<string name="error_retrieving_oauth_token">无法获取登录信息。</string>
|
||||
<string name="error_compose_character_limit">嘟文太长了!</string>
|
||||
<string name="error_media_upload_size">文件大小限制 4MB。</string>
|
||||
<string name="error_media_upload_size">文件大小限制 8MB。</string>
|
||||
<string name="error_media_upload_type">无法上传此类型的文件。</string>
|
||||
<string name="error_media_upload_opening">此文件无法打开。</string>
|
||||
<string name="error_media_upload_permission">需要授予 Tusky 读取媒体文件的权限。</string>
|
||||
|
@ -71,7 +71,6 @@
|
|||
<string name="action_send">发嘟</string>
|
||||
<string name="action_send_public">发嘟</string>
|
||||
<string name="action_retry">重试</string>
|
||||
<string name="action_hide_text">隐藏文字内容</string>
|
||||
<string name="action_close">关闭</string>
|
||||
<string name="action_view_profile">个人资料</string>
|
||||
<string name="action_view_preferences">设置</string>
|
||||
|
@ -81,14 +80,13 @@
|
|||
<string name="action_view_follow_requests">关注请求</string>
|
||||
<string name="action_view_media">媒体</string>
|
||||
<string name="action_open_in_web">在浏览器中打开</string>
|
||||
<string name="action_photo_pick">从相册中选择</string>
|
||||
<string name="action_add_media">从相册中选择</string>
|
||||
<string name="action_photo_take">拍照</string>
|
||||
<string name="action_share">分享</string>
|
||||
<string name="action_mute">隐藏</string>
|
||||
<string name="action_unmute">取消隐藏</string>
|
||||
<string name="action_mention">提及</string>
|
||||
<string name="action_hide_media">隐藏媒体文件</string>
|
||||
<string name="action_compose_options">选项</string>
|
||||
<string name="action_open_drawer">打开应用抽屉</string>
|
||||
<string name="action_save">保存</string>
|
||||
<string name="action_edit_profile">编辑个人资料</string>
|
||||
|
@ -105,7 +103,6 @@
|
|||
<string name="send_status_link_to">分享链接到…</string>
|
||||
<string name="send_status_content_to">分享嘟文到…</string>
|
||||
|
||||
<string name="confirmation_send">嘟文已发送!</string>
|
||||
<string name="confirmation_reported">报告已发送!</string>
|
||||
<string name="confirmation_unblocked">用户已被屏蔽</string>
|
||||
<string name="confirmation_unmuted">用户已被隐藏</string>
|
||||
|
@ -136,7 +133,6 @@
|
|||
<string name="dialog_download_image">下载</string>
|
||||
<string name="dialog_message_follow_request">关注请求已发送,等待对方回复</string>
|
||||
<string name="dialog_unfollow_warning">不再关注此用户?</string>
|
||||
<string name="dialog_reply_not_found">回复发表失败,被回复的嘟文不可用。是否转换成普通嘟文?</string>
|
||||
|
||||
<string name="visibility_public">公开:所有人可见,并会出现在公共时间轴上</string>
|
||||
<string name="visibility_unlisted">不公开:所有人可见,但不会出现在公共时间轴上</string>
|
||||
|
@ -247,7 +243,6 @@
|
|||
<string name="state_follow_requested">已发送关注请求</string>
|
||||
|
||||
<string name="no_content">无内容</string>
|
||||
<string name="action_save_one_toot">嘟文已保存。</string>
|
||||
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">%d 年内</string>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">授權被拒絕。</string>
|
||||
<string name="error_retrieving_oauth_token">無法獲取登錄信息。</string>
|
||||
<string name="error_compose_character_limit">嘟文太長了!</string>
|
||||
<string name="error_media_upload_size">文件大小限制 4MB。</string>
|
||||
<string name="error_media_upload_size">文件大小限制 8MB。</string>
|
||||
<string name="error_media_upload_type">無法上傳此類型的文件。</string>
|
||||
<string name="error_media_upload_opening">此文件無法打開。</string>
|
||||
<string name="error_media_upload_permission">需要授予 Tusky 讀取媒體文件的權限。</string>
|
||||
|
@ -71,7 +71,6 @@
|
|||
<string name="action_send">發嘟</string>
|
||||
<string name="action_send_public">發嘟</string>
|
||||
<string name="action_retry">重試</string>
|
||||
<string name="action_hide_text">隱藏文字內容</string>
|
||||
<string name="action_close">關閉</string>
|
||||
<string name="action_view_profile">個人資料</string>
|
||||
<string name="action_view_preferences">設置</string>
|
||||
|
@ -81,14 +80,13 @@
|
|||
<string name="action_view_follow_requests">關注請求</string>
|
||||
<string name="action_view_media">媒體</string>
|
||||
<string name="action_open_in_web">在瀏覽器中打開</string>
|
||||
<string name="action_photo_pick">從相冊中選擇</string>
|
||||
<string name="action_add_media">從相冊中選擇</string>
|
||||
<string name="action_photo_take">拍照</string>
|
||||
<string name="action_share">分享</string>
|
||||
<string name="action_mute">隱藏</string>
|
||||
<string name="action_unmute">取消隱藏</string>
|
||||
<string name="action_mention">提及</string>
|
||||
<string name="action_hide_media">隱藏媒體文件</string>
|
||||
<string name="action_compose_options">選項</string>
|
||||
<string name="action_open_drawer">打開應用抽屜</string>
|
||||
<string name="action_save">保存</string>
|
||||
<string name="action_edit_profile">編輯個人資料</string>
|
||||
|
@ -105,7 +103,6 @@
|
|||
<string name="send_status_link_to">分享鏈接到…</string>
|
||||
<string name="send_status_content_to">分享嘟文到…</string>
|
||||
|
||||
<string name="confirmation_send">嘟文已發送!</string>
|
||||
<string name="confirmation_reported">報告已發送!</string>
|
||||
<string name="confirmation_unblocked">用户已被屏蔽</string>
|
||||
<string name="confirmation_unmuted">用户已被隱藏</string>
|
||||
|
@ -136,7 +133,6 @@
|
|||
<string name="dialog_download_image">下載</string>
|
||||
<string name="dialog_message_follow_request">關注請求已發送,等待對方回覆</string>
|
||||
<string name="dialog_unfollow_warning">不再關注此用户?</string>
|
||||
<string name="dialog_reply_not_found">回覆發表失敗,被回覆的嘟文不可用。是否轉換成普通嘟文?</string>
|
||||
|
||||
<string name="visibility_public">公開:所有人可見,並會出現在公共時間軸上</string>
|
||||
<string name="visibility_unlisted">不公開:所有人可見,但不會出現在公共時間軸上</string>
|
||||
|
@ -247,7 +243,6 @@
|
|||
<string name="state_follow_requested">已發送關注請求</string>
|
||||
|
||||
<string name="no_content">無內容</string>
|
||||
<string name="action_save_one_toot">嘟文已保存。</string>
|
||||
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">%d 年內</string>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">授權被拒絕。</string>
|
||||
<string name="error_retrieving_oauth_token">無法獲取登錄信息。</string>
|
||||
<string name="error_compose_character_limit">嘟文太長了!</string>
|
||||
<string name="error_media_upload_size">文件大小限制 4MB。</string>
|
||||
<string name="error_media_upload_size">文件大小限制 8MB。</string>
|
||||
<string name="error_media_upload_type">無法上傳此類型的文件。</string>
|
||||
<string name="error_media_upload_opening">此文件無法打開。</string>
|
||||
<string name="error_media_upload_permission">需要授予 Tusky 讀取媒體文件的權限。</string>
|
||||
|
@ -71,7 +71,6 @@
|
|||
<string name="action_send">發嘟</string>
|
||||
<string name="action_send_public">發嘟</string>
|
||||
<string name="action_retry">重試</string>
|
||||
<string name="action_hide_text">隱藏文字內容</string>
|
||||
<string name="action_close">關閉</string>
|
||||
<string name="action_view_profile">個人資料</string>
|
||||
<string name="action_view_preferences">設置</string>
|
||||
|
@ -81,14 +80,13 @@
|
|||
<string name="action_view_follow_requests">關注請求</string>
|
||||
<string name="action_view_media">媒體</string>
|
||||
<string name="action_open_in_web">在瀏覽器中打開</string>
|
||||
<string name="action_photo_pick">從相冊中選擇</string>
|
||||
<string name="action_add_media">從相冊中選擇</string>
|
||||
<string name="action_photo_take">拍照</string>
|
||||
<string name="action_share">分享</string>
|
||||
<string name="action_mute">隱藏</string>
|
||||
<string name="action_unmute">取消隱藏</string>
|
||||
<string name="action_mention">提及</string>
|
||||
<string name="action_hide_media">隱藏媒體文件</string>
|
||||
<string name="action_compose_options">選項</string>
|
||||
<string name="action_open_drawer">打開應用抽屜</string>
|
||||
<string name="action_save">保存</string>
|
||||
<string name="action_edit_profile">編輯個人資料</string>
|
||||
|
@ -105,7 +103,6 @@
|
|||
<string name="send_status_link_to">分享鏈接到…</string>
|
||||
<string name="send_status_content_to">分享嘟文到…</string>
|
||||
|
||||
<string name="confirmation_send">嘟文已發送!</string>
|
||||
<string name="confirmation_reported">報告已發送!</string>
|
||||
<string name="confirmation_unblocked">用户已被屏蔽</string>
|
||||
<string name="confirmation_unmuted">用户已被隱藏</string>
|
||||
|
@ -136,7 +133,6 @@
|
|||
<string name="dialog_download_image">下載</string>
|
||||
<string name="dialog_message_follow_request">關注請求已發送,等待對方回覆</string>
|
||||
<string name="dialog_unfollow_warning">不再關注此用户?</string>
|
||||
<string name="dialog_reply_not_found">回覆發表失敗,被回覆的嘟文不可用。是否轉換成普通嘟文?</string>
|
||||
|
||||
<string name="visibility_public">公開:所有人可見,並會出現在公共時間軸上</string>
|
||||
<string name="visibility_unlisted">不公開:所有人可見,但不會出現在公共時間軸上</string>
|
||||
|
@ -247,7 +243,6 @@
|
|||
<string name="state_follow_requested">已發送關注請求</string>
|
||||
|
||||
<string name="no_content">無內容</string>
|
||||
<string name="action_save_one_toot">嘟文已保存。</string>
|
||||
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">%d 年內</string>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">授权被拒绝。</string>
|
||||
<string name="error_retrieving_oauth_token">无法获取登录信息。</string>
|
||||
<string name="error_compose_character_limit">嘟文太长了!</string>
|
||||
<string name="error_media_upload_size">文件大小限制 4MB。</string>
|
||||
<string name="error_media_upload_size">文件大小限制 8MB。</string>
|
||||
<string name="error_media_upload_type">无法上传此类型的文件。</string>
|
||||
<string name="error_media_upload_opening">此文件无法打开。</string>
|
||||
<string name="error_media_upload_permission">需要授予 Tusky 读取媒体文件的权限。</string>
|
||||
|
@ -71,7 +71,6 @@
|
|||
<string name="action_send">发嘟</string>
|
||||
<string name="action_send_public">发嘟</string>
|
||||
<string name="action_retry">重试</string>
|
||||
<string name="action_hide_text">隐藏文字内容</string>
|
||||
<string name="action_close">关闭</string>
|
||||
<string name="action_view_profile">个人资料</string>
|
||||
<string name="action_view_preferences">设置</string>
|
||||
|
@ -81,14 +80,13 @@
|
|||
<string name="action_view_follow_requests">关注请求</string>
|
||||
<string name="action_view_media">媒体</string>
|
||||
<string name="action_open_in_web">在浏览器中打开</string>
|
||||
<string name="action_photo_pick">从相册中选择</string>
|
||||
<string name="action_add_media">从相册中选择</string>
|
||||
<string name="action_photo_take">拍照</string>
|
||||
<string name="action_share">分享</string>
|
||||
<string name="action_mute">隐藏</string>
|
||||
<string name="action_unmute">取消隐藏</string>
|
||||
<string name="action_mention">提及</string>
|
||||
<string name="action_hide_media">隐藏媒体文件</string>
|
||||
<string name="action_compose_options">选项</string>
|
||||
<string name="action_open_drawer">打开应用抽屉</string>
|
||||
<string name="action_save">保存</string>
|
||||
<string name="action_edit_profile">编辑个人资料</string>
|
||||
|
@ -105,7 +103,6 @@
|
|||
<string name="send_status_link_to">分享链接到…</string>
|
||||
<string name="send_status_content_to">分享嘟文到…</string>
|
||||
|
||||
<string name="confirmation_send">嘟文已发送!</string>
|
||||
<string name="confirmation_reported">报告已发送!</string>
|
||||
<string name="confirmation_unblocked">用户已被屏蔽</string>
|
||||
<string name="confirmation_unmuted">用户已被隐藏</string>
|
||||
|
@ -136,7 +133,6 @@
|
|||
<string name="dialog_download_image">下载</string>
|
||||
<string name="dialog_message_follow_request">关注请求已发送,等待对方回复</string>
|
||||
<string name="dialog_unfollow_warning">不再关注此用户?</string>
|
||||
<string name="dialog_reply_not_found">回复发表失败,被回复的嘟文不可用。是否转换成普通嘟文?</string>
|
||||
|
||||
<string name="visibility_public">公开:所有人可见,并会出现在公共时间轴上</string>
|
||||
<string name="visibility_unlisted">不公开:所有人可见,但不会出现在公共时间轴上</string>
|
||||
|
@ -247,7 +243,6 @@
|
|||
<string name="state_follow_requested">已发送关注请求</string>
|
||||
|
||||
<string name="no_content">无内容</string>
|
||||
<string name="action_save_one_toot">嘟文已保存。</string>
|
||||
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">%d 年内</string>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">授權被拒絕。</string>
|
||||
<string name="error_retrieving_oauth_token">無法獲取登錄信息。</string>
|
||||
<string name="error_compose_character_limit">嘟文太長了!</string>
|
||||
<string name="error_media_upload_size">文件大小限制 4MB。</string>
|
||||
<string name="error_media_upload_size">文件大小限制 8MB。</string>
|
||||
<string name="error_media_upload_type">無法上傳此類型的文件。</string>
|
||||
<string name="error_media_upload_opening">此文件無法打開。</string>
|
||||
<string name="error_media_upload_permission">需要授予 Tusky 讀取媒體文件的權限。</string>
|
||||
|
@ -71,7 +71,6 @@
|
|||
<string name="action_send">發嘟</string>
|
||||
<string name="action_send_public">發嘟</string>
|
||||
<string name="action_retry">重試</string>
|
||||
<string name="action_hide_text">隱藏文字內容</string>
|
||||
<string name="action_close">關閉</string>
|
||||
<string name="action_view_profile">個人資料</string>
|
||||
<string name="action_view_preferences">設置</string>
|
||||
|
@ -81,14 +80,13 @@
|
|||
<string name="action_view_follow_requests">關注請求</string>
|
||||
<string name="action_view_media">媒體</string>
|
||||
<string name="action_open_in_web">在瀏覽器中打開</string>
|
||||
<string name="action_photo_pick">從相冊中選擇</string>
|
||||
<string name="action_add_media">從相冊中選擇</string>
|
||||
<string name="action_photo_take">拍照</string>
|
||||
<string name="action_share">分享</string>
|
||||
<string name="action_mute">隱藏</string>
|
||||
<string name="action_unmute">取消隱藏</string>
|
||||
<string name="action_mention">提及</string>
|
||||
<string name="action_hide_media">隱藏媒體文件</string>
|
||||
<string name="action_compose_options">選項</string>
|
||||
<string name="action_open_drawer">打開應用抽屜</string>
|
||||
<string name="action_save">保存</string>
|
||||
<string name="action_edit_profile">編輯個人資料</string>
|
||||
|
@ -105,7 +103,6 @@
|
|||
<string name="send_status_link_to">分享鏈接到…</string>
|
||||
<string name="send_status_content_to">分享嘟文到…</string>
|
||||
|
||||
<string name="confirmation_send">嘟文已發送!</string>
|
||||
<string name="confirmation_reported">報告已發送!</string>
|
||||
<string name="confirmation_unblocked">用戶已被屏蔽</string>
|
||||
<string name="confirmation_unmuted">用戶已被隱藏</string>
|
||||
|
@ -136,7 +133,6 @@
|
|||
<string name="dialog_download_image">下載</string>
|
||||
<string name="dialog_message_follow_request">關注請求已發送,等待對方回覆</string>
|
||||
<string name="dialog_unfollow_warning">不再關注此用戶?</string>
|
||||
<string name="dialog_reply_not_found">回覆發表失敗,被回覆的嘟文不可用。是否轉換成普通嘟文?</string>
|
||||
|
||||
<string name="visibility_public">公開:所有人可見,並會出現在公共時間軸上</string>
|
||||
<string name="visibility_unlisted">不公開:所有人可見,但不會出現在公共時間軸上</string>
|
||||
|
@ -247,7 +243,6 @@
|
|||
<string name="state_follow_requested">已發送關注請求</string>
|
||||
|
||||
<string name="no_content">無內容</string>
|
||||
<string name="action_save_one_toot">嘟文已保存。</string>
|
||||
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">%d 年內</string>
|
||||
|
|
|
@ -29,18 +29,15 @@
|
|||
<attr name="account_toolbar_icon_tint_collapsed" format="reference|color" />
|
||||
<attr name="account_toolbar_popup_theme" format="reference" />
|
||||
<attr name="compose_close_button_tint" format="reference|color" />
|
||||
<attr name="compose_media_button_tint" format="reference|color" />
|
||||
<attr name="compose_media_button_disabled_tint" format="reference|color" />
|
||||
<attr name="compose_mention_color" format="reference|color" />
|
||||
<attr name="compose_content_warning_bar_background" format="reference" />
|
||||
<attr name="compose_hide_media_button_color" format="reference|color" />
|
||||
<attr name="compose_hide_media_button_selected_color" format="reference|color" />
|
||||
<attr name="compose_image_button_tint" format="reference|color" />
|
||||
<attr name="compose_reply_content_background" format="reference|color" />
|
||||
<attr name="report_status_background_color" format="reference|color" />
|
||||
<attr name="report_status_divider_drawable" format="reference" />
|
||||
<attr name="card_background" format="reference|color" />
|
||||
<attr name="card_image_background" format="reference|color" />
|
||||
<attr name="compound_button_color" format="reference" />
|
||||
|
||||
<attr name="play_indicator_drawable" format="reference" />
|
||||
<attr name="status_text_small" format="dimension" />
|
||||
|
|
|
@ -34,8 +34,7 @@
|
|||
<color name="tab_page_margin_dark">#4C534B</color>
|
||||
<color name="account_toolbar_icon_collapsed_dark">#FFFFFF</color>
|
||||
<color name="account_header_background_dark">#000000</color>
|
||||
<color name="compose_media_button_dark">#d9e1e8</color>
|
||||
<color name="compose_media_button_disabled_dark">#8F8F8F</color>
|
||||
<color name="compose_media_button_disabled_dark">#586173</color>
|
||||
<color name="compose_mention_dark">#AFBFCF</color>
|
||||
<color name="report_status_background_dark">#000000</color>
|
||||
<color name="report_status_divider_dark">#2F2F2F</color>
|
||||
|
@ -57,7 +56,6 @@
|
|||
<color name="text_color_tertiary_inverse_light">#ffffff</color>
|
||||
<color name="toolbar_background_light">#f6f7f7</color>
|
||||
<color name="toolbar_icon_light">#7C000000</color>
|
||||
<color name="image_button_light">#4f4f4f</color>
|
||||
<color name="status_reblog_button_light">#4f4f4f</color>
|
||||
<color name="status_reblog_button_marked_light">#56a7e1</color>
|
||||
<color name="status_reblog_button_disabled_light">#BFBFBF</color>
|
||||
|
@ -69,12 +67,13 @@
|
|||
<color name="tab_page_margin_light">#9F9F9F</color>
|
||||
<color name="account_toolbar_icon_collapsed_light">#DE000000</color>
|
||||
<color name="account_header_background_light">#EFEFEF</color>
|
||||
<color name="compose_media_button_light">#4f4f4f</color>
|
||||
<color name="compose_media_button_disabled_light">#8F8F8F</color>
|
||||
<color name="compose_media_button_disabled_light">#a3a5ab</color>
|
||||
<color name="compose_mention_light">#2F5F6F</color>
|
||||
<color name="report_status_background_light">#EFEFEF</color>
|
||||
<color name="report_status_divider_light">#9F9F9F</color>
|
||||
<color name="custom_tab_toolbar_light">#ffffff</color>
|
||||
<color name="compose_reply_content_background_light">#e0e1e6</color>
|
||||
|
||||
<color name="compose_media_visible_button_disabled_blue">#8c2b90d9</color>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -18,4 +18,6 @@
|
|||
<dimen name="card_image_vertical_height">160dp</dimen>
|
||||
<dimen name="card_image_horizontal_width">100dp</dimen>
|
||||
|
||||
<dimen name="compose_activity_snackbar_elevation">16dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<string name="error_authorization_denied">Authorization was denied.</string>
|
||||
<string name="error_retrieving_oauth_token">Failed getting a login token.</string>
|
||||
<string name="error_compose_character_limit">The status is too long!</string>
|
||||
<string name="error_media_upload_size">The file must be less than 4MB.</string>
|
||||
<string name="error_media_upload_size">The file must be less than 8MB.</string>
|
||||
<string name="error_media_upload_type">That type of file cannot be uploaded.</string>
|
||||
<string name="error_media_upload_opening">That file could not be opened.</string>
|
||||
<string name="error_media_upload_permission">Permission to read media is required.</string>
|
||||
|
@ -73,7 +73,6 @@
|
|||
<string name="action_send">TOOT</string>
|
||||
<string name="action_send_public">TOOT!</string>
|
||||
<string name="action_retry">Retry</string>
|
||||
<string name="action_hide_text">Hide text behind warning</string>
|
||||
<string name="action_close">Close</string>
|
||||
<string name="action_view_profile">Profile</string>
|
||||
<string name="action_view_preferences">Preferences</string>
|
||||
|
@ -83,14 +82,13 @@
|
|||
<string name="action_view_follow_requests">Follow Requests</string>
|
||||
<string name="action_view_media">Media</string>
|
||||
<string name="action_open_in_web">Open in browser</string>
|
||||
<string name="action_photo_pick">Add media</string>
|
||||
<string name="action_add_media">Add media</string>
|
||||
<string name="action_photo_take">Take photo</string>
|
||||
<string name="action_share">Share</string>
|
||||
<string name="action_mute">Mute</string>
|
||||
<string name="action_unmute">Unmute</string>
|
||||
<string name="action_mention">Mention</string>
|
||||
<string name="action_hide_media">Hide media</string>
|
||||
<string name="action_compose_options">Options</string>
|
||||
<string name="action_open_drawer">Open drawer</string>
|
||||
<string name="action_save">Save</string>
|
||||
<string name="action_edit_profile">Edit profile</string>
|
||||
|
@ -99,6 +97,9 @@
|
|||
<string name="action_reject">Reject</string>
|
||||
<string name="action_search">Search</string>
|
||||
<string name="action_access_saved_toot">Drafts</string>
|
||||
<string name="action_toggle_visibility">Toot visibility</string>
|
||||
<string name="action_content_warning">Content warning</string>
|
||||
<string name="action_emoji_keyboard">Emoji keyboard</string>
|
||||
|
||||
<string name="download_image">Downloading %1$s</string>
|
||||
|
||||
|
@ -107,7 +108,6 @@
|
|||
<string name="send_status_link_to">Share toot URL to…</string>
|
||||
<string name="send_status_content_to">Share toot to…</string>
|
||||
|
||||
<string name="confirmation_send">Toot!</string>
|
||||
<string name="confirmation_reported">Sent!</string>
|
||||
<string name="confirmation_unblocked">User unblocked</string>
|
||||
<string name="confirmation_unmuted">User unmuted</string>
|
||||
|
@ -142,7 +142,6 @@
|
|||
<string name="dialog_download_image">Download</string>
|
||||
<string name="dialog_message_follow_request">Follow request pending: awaiting their response</string>
|
||||
<string name="dialog_unfollow_warning">Unfollow this account?</string>
|
||||
<string name="dialog_reply_not_found">Couldn\'t post this status. The status you\'re replying to might not be available. Remove reply info?</string>
|
||||
|
||||
<string name="visibility_public">Public: Post to public timelines</string>
|
||||
<string name="visibility_unlisted">Unlisted: Do not show in public timelines</string>
|
||||
|
@ -256,7 +255,6 @@
|
|||
<string name="state_follow_requested">Follow requested</string>
|
||||
|
||||
<string name="no_content">no content</string>
|
||||
<string name="action_save_one_toot">Toot saved!</string>
|
||||
|
||||
<!--These are for timestamps on statuses. For example: "16s" or "2d"-->
|
||||
<string name="abbreviated_in_years">in %dy</string>
|
||||
|
@ -291,5 +289,11 @@
|
|||
<string name="action_remove_media">Remove</string>
|
||||
<string name="lock_account_label">Lock account</string>
|
||||
<string name="lock_account_label_description">Requires you to manually approve followers</string>
|
||||
<string name="compose_save_draft">Save draft?</string>
|
||||
<string name="send_toot_notification_title">Sending Toot...</string>
|
||||
<string name="send_toot_notification_error_title">Error sending toot</string>
|
||||
<string name="send_toot_notification_channel_name">Sending Toots</string>
|
||||
<string name="send_toot_notification_cancel_title">Sending cancelled</string>
|
||||
<string name="send_toot_notification_saved_content">A copy of the toot has been saved to your drafts</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -76,13 +76,9 @@
|
|||
<item name="account_toolbar_icon_tint_collapsed">@color/account_toolbar_icon_collapsed_light</item>
|
||||
<item name="account_toolbar_popup_theme">@style/AppTheme.Account.ToolbarPopupTheme.Light</item>
|
||||
<item name="compose_close_button_tint">@color/toolbar_icon_light</item>
|
||||
<item name="compose_media_button_tint">@color/compose_media_button_light</item>
|
||||
<item name="compose_media_button_disabled_tint">@color/compose_media_button_disabled_light</item>
|
||||
<item name="compose_mention_color">@color/compose_mention_light</item>
|
||||
<item name="compose_content_warning_bar_background">@drawable/border_background_light</item>
|
||||
<item name="compose_hide_media_button_color">@color/image_button_light</item>
|
||||
<item name="compose_hide_media_button_selected_color">@color/color_accent_light</item>
|
||||
<item name="compose_image_button_tint">@color/image_button_light</item>
|
||||
<item name="compose_reply_content_background">@color/compose_reply_content_background_light</item>
|
||||
|
||||
<item name="report_status_background_color">@color/report_status_background_light</item>
|
||||
|
@ -103,10 +99,13 @@
|
|||
|
||||
<item name="play_indicator_drawable">@drawable/ic_play_indicator_light</item>
|
||||
|
||||
<item name="compound_button_color">@color/compound_button_color_light</item>
|
||||
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.ImageButton.Light" parent="Widget.AppCompat.Button.Borderless.Colored">
|
||||
<item name="android:tint">@color/image_button_light</item>
|
||||
<item name="android:tint">@color/text_color_tertiary_light</item>
|
||||
<item name="android:background">?attr/selectableItemBackgroundBorderless</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.BottomSheetDialog.Light" parent="@style/Theme.Design.Light.BottomSheetDialog">
|
||||
|
|
7
app/src/main/res/values/toot_button.xml
Normal file
7
app/src/main/res/values/toot_button.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<bool name="show_small_toot_button">true</bool>
|
||||
<dimen name="toot_button_width">48dp</dimen>
|
||||
<dimen name="toot_button_drawable_padding">4dp</dimen>
|
||||
|
||||
</resources>
|
|
@ -19,6 +19,9 @@ package com.keylesspalace.tusky
|
|||
import android.widget.EditText
|
||||
import com.keylesspalace.tusky.db.AccountEntity
|
||||
import com.keylesspalace.tusky.db.AccountManager
|
||||
import com.keylesspalace.tusky.entity.Emoji
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import okhttp3.Request
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
|
@ -30,6 +33,9 @@ import org.robolectric.Robolectric
|
|||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.fakes.RoboMenuItem
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
/**
|
||||
* Created by charlag on 3/7/18.
|
||||
|
@ -43,6 +49,7 @@ class ComposeActivityTest {
|
|||
lateinit var application: FakeTuskyApplication
|
||||
lateinit var serviceLocator: TuskyApplication.ServiceLocator
|
||||
lateinit var accountManagerMock: AccountManager
|
||||
lateinit var apiMock: MastodonApi
|
||||
|
||||
val account = AccountEntity(
|
||||
id = 1,
|
||||
|
@ -67,13 +74,44 @@ class ComposeActivityTest {
|
|||
fun before() {
|
||||
val controller = Robolectric.buildActivity(ComposeActivity::class.java)
|
||||
activity = controller.get()
|
||||
|
||||
accountManagerMock = Mockito.mock(AccountManager::class.java)
|
||||
serviceLocator = Mockito.mock(TuskyApplication.ServiceLocator::class.java)
|
||||
`when`(serviceLocator.get(AccountManager::class.java)).thenReturn(accountManagerMock)
|
||||
`when`(accountManagerMock.activeAccount).thenReturn(account)
|
||||
|
||||
apiMock = Mockito.mock(MastodonApi::class.java)
|
||||
`when`(apiMock.customEmojis).thenReturn(object: Call<List<Emoji>> {
|
||||
override fun isExecuted(): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun clone(): Call<List<Emoji>> {
|
||||
throw Error("not implemented")
|
||||
}
|
||||
override fun isCanceled(): Boolean {
|
||||
throw Error("not implemented")
|
||||
}
|
||||
override fun cancel() {
|
||||
throw Error("not implemented")
|
||||
}
|
||||
override fun execute(): Response<List<Emoji>> {
|
||||
throw Error("not implemented")
|
||||
}
|
||||
override fun request(): Request {
|
||||
throw Error("not implemented")
|
||||
}
|
||||
|
||||
override fun enqueue(callback: Callback<List<Emoji>>?) {}
|
||||
})
|
||||
|
||||
activity.mastodonApi = apiMock
|
||||
activity.accountManager = accountManagerMock
|
||||
application = activity.application as FakeTuskyApplication
|
||||
application.locator = serviceLocator
|
||||
|
||||
`when`(accountManagerMock.activeAccount).thenReturn(account)
|
||||
|
||||
|
||||
controller.create().start()
|
||||
}
|
||||
|
||||
|
@ -115,6 +153,6 @@ class ComposeActivityTest {
|
|||
}
|
||||
|
||||
private fun insertSomeTextInContent() {
|
||||
activity.findViewById<EditText>(R.id.compose_edit_field).setText("Some text")
|
||||
activity.findViewById<EditText>(R.id.composeEditField).setText("Some text")
|
||||
}
|
||||
}
|
|
@ -8,6 +8,10 @@ class FakeTuskyApplication : TuskyApplication() {
|
|||
|
||||
lateinit var locator: ServiceLocator
|
||||
|
||||
override fun initAppInjector() {
|
||||
// No-op
|
||||
}
|
||||
|
||||
override fun initPicasso() {
|
||||
// No-op
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue