1
0
mirror of https://github.com/tuskyapp/Tusky synced 2024-12-18 03:48:53 +01:00

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:
Konrad Pozniak 2018-04-13 22:37:21 +02:00 committed by GitHub
parent 8a23f034f0
commit 27eefbf65a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
79 changed files with 1815 additions and 1234 deletions

View File

@ -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'
})

View File

@ -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"

View File

@ -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;

View File

@ -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()) {

View File

@ -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) {

View File

@ -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);
}

View File

@ -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)
}

View File

@ -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);

View File

@ -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);

View File

@ -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
/**

View File

@ -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`))");
}
};
}

View 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?
}

View File

@ -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)
}
}

View File

@ -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);
}

View File

@ -31,7 +31,8 @@ import javax.inject.Singleton
AppModule::class,
NetworkModule::class,
AndroidInjectionModule::class,
ActivitiesModule::class
ActivitiesModule::class,
ServicesModule::class
])
interface AppComponent {
@Component.Builder

View File

@ -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

View File

@ -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
}

View 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

View File

@ -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
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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));
}
}
}
/**
* AB={xA|xB}
*
* @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;
}
}

View File

@ -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)
}

View File

@ -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;
}
}
}

View File

@ -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
}
}
}

View 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)
}
}

View File

@ -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;
}

View 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>

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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" />

View File

@ -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"

View File

@ -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"

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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 dauthentification.</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 nest 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>

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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" />

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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">

View 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>

View File

@ -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")
}
}

View File

@ -8,6 +8,10 @@ class FakeTuskyApplication : TuskyApplication() {
lateinit var locator: ServiceLocator
override fun initAppInjector() {
// No-op
}
override fun initPicasso() {
// No-op
}