New settings (#891)

* change drawer items

* rename SettingsActivity

* introduce AccountSettings activity

* improve account settings, move notification settings

* sync settings with server

* rename settings back to preferences

* add functionality for settings

* move mediaPreviewEnabled preference to AccountPreferences

* replace shared prefs with accountmanager

* move PreferencesFragment to support library

* split preferences fragment into smaller fragments,
merge AccountPreferencesActivity into PreferencesFragment

* adjust icon size, add icons to general preferences

* change mediaPreviewEnabled and alwaysShowSensitiveMedia pref position

* add database migration

* remove pullNotificationCheckInterval option

* fix  preference in TimelineFragment

* Update Chinese translations. (#915)

* Update zh-CN translations.

* Update zh-SG translations.

* Update zh-TW translations.

* Update zh-MO translations.

* Update zh-HK translations.

* Fix errors in zh-CN translations.

* Fix errors in zh-SG translations.

* Fix errors in zh-TW translations.

* Fix errors in zh-MO translations.

* Fix errors in zh-HK translations.
This commit is contained in:
Konrad Pozniak 2018-11-12 21:09:39 +01:00 committed by GitHub
parent 5c88edf276
commit 70ca42c8b7
65 changed files with 1636 additions and 1083 deletions

View File

@ -74,6 +74,7 @@ dependencies {
implementation "com.android.support:design:$supportLibraryVersion"
implementation "com.android.support:exifinterface:$supportLibraryVersion"
implementation "com.android.support:cardview-v7:$supportLibraryVersion"
implementation "com.android.support:preference-v7:$supportLibraryVersion"
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.picasso:picasso:2.5.2'

View File

@ -52,7 +52,7 @@ public final class AccountListActivity extends BaseActivity implements HasSuppor
return intent;
}
enum Type {
public enum Type {
BLOCKS,
MUTES,
FOLLOW_REQUESTS,

View File

@ -15,7 +15,6 @@
package com.keylesspalace.tusky;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
@ -32,8 +31,6 @@ import android.util.TypedValue;
import android.view.Menu;
import android.view.View;
import com.evernote.android.job.JobManager;
import com.evernote.android.job.JobRequest;
import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.di.Injectable;
@ -122,10 +119,6 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
super.finish();
}
protected SharedPreferences getPrivatePreferences() {
return getSharedPreferences(getString(R.string.preferences_file_key), Context.MODE_PRIVATE);
}
protected void redirectIfNotLoggedIn() {
AccountEntity account = accountManager.getActiveAccount();
if (account == null) {
@ -154,34 +147,6 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
return super.onCreateOptionsMenu(menu);
}
protected void enablePushNotifications() {
// schedule job to pull notifications
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
String minutesString = preferences.getString("pullNotificationCheckInterval", "15");
long minutes = Long.valueOf(minutesString);
if (minutes < 15) {
preferences.edit().putString("pullNotificationCheckInterval", "15").apply();
minutes = 15;
}
setPullNotificationCheckInterval(minutes);
}
protected void disablePushNotifications() {
// Cancel the repeating call for "pull" notifications.
JobManager.instance().cancelAllForTag(NotificationPullJobCreator.NOTIFICATIONS_JOB_TAG);
}
protected void setPullNotificationCheckInterval(long minutes) {
long checkInterval = 1000 * 60 * minutes;
new JobRequest.Builder(NotificationPullJobCreator.NOTIFICATIONS_JOB_TAG)
.setPeriodic(checkInterval)
.setUpdateCurrent(true)
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.build()
.scheduleAsync();
}
protected void showErrorDialog(View anyView, @StringRes int descriptionId, @StringRes int actionId, View.OnClickListener listener) {
if (anyView != null) {
Snackbar bar = Snackbar.make(anyView, getString(descriptionId), Snackbar.LENGTH_SHORT);

View File

@ -390,7 +390,7 @@ public final class ComposeActivity
}
photoUploadUri = savedInstanceState.getParcelable("photoUploadUri");
} else {
statusMarkSensitive = false;
statusMarkSensitive = activeAccount.getDefaultMediaSensitivity();
startingHideText = false;
photoUploadUri = null;
}
@ -406,9 +406,7 @@ public final class ComposeActivity
if (intent != null) {
if (startingVisibility == Status.Visibility.UNKNOWN) {
Status.Visibility preferredVisibility = Status.Visibility.byString(
preferences.getString("defaultPostPrivacy",
Status.Visibility.PUBLIC.serverString()));
Status.Visibility preferredVisibility = activeAccount.getDefaultPostPrivacy();
Status.Visibility replyVisibility = Status.Visibility.byNum(
intent.getIntExtra(REPLY_VISIBILITY_EXTRA, Status.Visibility.UNKNOWN.getNum()));

View File

@ -1,15 +1,17 @@
package com.keylesspalace.tusky;
import android.app.AlarmManager;
import android.support.v7.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.preference.DialogPreference;
import android.preference.PreferenceManager;
import android.support.v7.app.AlertDialog;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
@ -25,9 +27,8 @@ import java.util.ArrayList;
/**
* This Preference lets the user select their preferred emoji font
*/
public class EmojiPreference extends DialogPreference {
public class EmojiPreference extends Preference {
private static final String TAG = "EmojiPreference";
private final Context context;
private EmojiCompatFont selected, original;
static final String FONT_PREFERENCE = "selected_emoji_font";
private static final EmojiCompatFont[] FONTS = EmojiCompatFont.FONTS;
@ -42,13 +43,6 @@ public class EmojiPreference extends DialogPreference {
public EmojiPreference(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
setDialogLayoutResource(R.layout.dialog_emojicompat);
setPositiveButtonText(android.R.string.ok);
setNegativeButtonText(android.R.string.cancel);
setDialogIcon(null);
// Find out which font is currently active
this.selected = EmojiCompatFont.byId(PreferenceManager
@ -60,27 +54,31 @@ public class EmojiPreference extends DialogPreference {
setSummary(selected.getDisplay(context));
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
protected void onClick() {
View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_emojicompat, null);
for(int i = 0; i < viewIds.length; i++) {
setupItem(view.findViewById(viewIds[i]), FONTS[i]);
}
new AlertDialog.Builder(getContext())
.setView(view)
.setPositiveButton(android.R.string.ok, (dialog, which) -> onDialogOk())
.setNegativeButton(android.R.string.cancel, null)
.show();
}
private void setupItem(View container, EmojiCompatFont font) {
Context context = container.getContext();
TextView title = container.findViewById(R.id.emojicompat_name);
TextView caption = container.findViewById(R.id.emojicompat_caption);
ImageView thumb = container.findViewById(R.id.emojicompat_thumb);
ImageButton download = container.findViewById(R.id.emojicompat_download);
ImageButton cancel = container.findViewById(R.id.emojicompat_download_cancel);
RadioButton radio = container.findViewById(R.id.emojicompat_radio);
TextView title = container.findViewById(R.id.emojicompat_name);
TextView caption = container.findViewById(R.id.emojicompat_caption);
ImageView thumb = container.findViewById(R.id.emojicompat_thumb);
ImageButton download = container.findViewById(R.id.emojicompat_download);
ImageButton cancel = container.findViewById(R.id.emojicompat_download_cancel);
RadioButton radio = container.findViewById(R.id.emojicompat_radio);
// Initialize all the views
title.setText(font.getDisplay(context));
@ -122,7 +120,7 @@ public class EmojiPreference extends DialogPreference {
cancel.setVisibility(View.VISIBLE);
font.downloadFont(context, new EmojiCompatFont.Downloader.EmojiDownloadListener() {
font.downloadFont(getContext(), new EmojiCompatFont.Downloader.EmojiDownloadListener() {
@Override
public void onDownloaded(EmojiCompatFont font) {
finishDownload(font, container);
@ -194,7 +192,7 @@ public class EmojiPreference extends DialogPreference {
cancel.setVisibility(View.GONE);
caption.setVisibility(View.VISIBLE);
if(font.isDownloaded(context)) {
if(font.isDownloaded(getContext())) {
// Make it selectable
download.setVisibility(View.GONE);
radio.setVisibility(View.VISIBLE);
@ -225,7 +223,7 @@ public class EmojiPreference extends DialogPreference {
Log.i(TAG, "saveSelectedFont: Font ID: " + index);
// It's saved using the key FONT_PREFERENCE
PreferenceManager
.getDefaultSharedPreferences(context)
.getDefaultSharedPreferences(getContext())
.edit()
.putInt(FONT_PREFERENCE, index)
.apply();
@ -235,29 +233,26 @@ public class EmojiPreference extends DialogPreference {
/**
* That's it. The user doesn't want to switch between these amazing radio buttons anymore!
* That means, the selected font can be saved (if the user hit OK)
* @param positiveResult if OK has been selected.
*/
@Override
public void onDialogClosed(boolean positiveResult) {
if(positiveResult) {
private void onDialogOk() {
saveSelectedFont();
if(selected != original) {
new AlertDialog.Builder(context)
new AlertDialog.Builder(getContext())
.setTitle(R.string.restart_required)
.setMessage(R.string.restart_emoji)
.setNegativeButton(R.string.later, null)
.setPositiveButton(R.string.restart, ((dialog, which) -> {
// Restart the app
// From https://stackoverflow.com/a/17166729/5070653
Intent launchIntent = new Intent(context, MainActivity.class);
Intent launchIntent = new Intent(getContext(), SplashActivity.class);
PendingIntent mPendingIntent = PendingIntent.getActivity(
context,
getContext(),
// This is the codepoint of the party face emoji :D
0x1f973,
launchIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr =
(AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
(AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
if (mgr != null) {
mgr.set(
AlarmManager.RTC,
@ -267,11 +262,7 @@ public class EmojiPreference extends DialogPreference {
System.exit(0);
})).show();
}
}
else {
// This line is needed in order to reset the radio buttons later
selected = original;
}
}
}

View File

@ -25,7 +25,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.TabLayout;
import android.support.graphics.drawable.VectorDrawableCompat;
import android.support.text.emoji.EmojiCompat;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
@ -85,15 +84,14 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
private static final long DRAWER_ITEM_ADD_ACCOUNT = -13;
private static final long DRAWER_ITEM_EDIT_PROFILE = 0;
private static final long DRAWER_ITEM_FAVOURITES = 1;
private static final long DRAWER_ITEM_MUTED_USERS = 2;
private static final long DRAWER_ITEM_BLOCKED_USERS = 3;
private static final long DRAWER_ITEM_SEARCH = 4;
private static final long DRAWER_ITEM_PREFERENCES = 5;
private static final long DRAWER_ITEM_ABOUT = 6;
private static final long DRAWER_ITEM_LOG_OUT = 7;
private static final long DRAWER_ITEM_FOLLOW_REQUESTS = 8;
private static final long DRAWER_ITEM_SAVED_TOOT = 9;
private static final long DRAWER_ITEM_LISTS = 10;
private static final long DRAWER_ITEM_LISTS = 2;
private static final long DRAWER_ITEM_SEARCH = 3;
private static final long DRAWER_ITEM_SAVED_TOOT = 4;
private static final long DRAWER_ITEM_ACCOUNT_SETTINGS = 5;
private static final long DRAWER_ITEM_SETTINGS = 6;
private static final long DRAWER_ITEM_ABOUT = 7;
private static final long DRAWER_ITEM_LOG_OUT = 8;
private static final long DRAWER_ITEM_FOLLOW_REQUESTS = 9;
@Inject
public DispatchingAndroidInjector<Fragment> fragmentInjector;
@ -215,9 +213,9 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
// Setup push notifications
if (NotificationHelper.areNotificationsEnabled(this, accountManager)) {
enablePushNotifications();
NotificationHelper.enablePullNotifications();
} else {
disablePushNotifications();
NotificationHelper.disablePullNotifications();
}
eventHub.getEvents()
@ -311,22 +309,17 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
}
});
VectorDrawableCompat muteDrawable = VectorDrawableCompat.create(getResources(),
R.drawable.ic_mute_24dp, getTheme());
ThemeUtils.setDrawableTint(this, muteDrawable, R.attr.toolbar_icon_tint);
List<IDrawerItem> listItems = new ArrayList<>(11);
List<IDrawerItem> listItems = new ArrayList<>(10);
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_EDIT_PROFILE).withName(R.string.action_edit_profile).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_person));
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_FAVOURITES).withName(R.string.action_view_favourites).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_star));
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_LISTS).withName(R.string.action_lists).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_list));
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_MUTED_USERS).withName(R.string.action_view_mutes).withSelectable(false).withIcon(muteDrawable));
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_BLOCKED_USERS).withName(R.string.action_view_blocks).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_block));
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SEARCH).withName(R.string.action_search).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_search));
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SAVED_TOOT).withName(R.string.action_access_saved_toot).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_save));
listItems.add(new PrimaryDrawerItem().withIdentifier(DRAWER_ITEM_SAVED_TOOT).withName(R.string.action_access_saved_toot).withSelectable(false).withIcon(R.drawable.ic_notebook).withIconTintingEnabled(true));
listItems.add(new DividerDrawerItem());
listItems.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_PREFERENCES).withName(R.string.action_view_preferences).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_settings));
listItems.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_ACCOUNT_SETTINGS).withName(R.string.action_view_account_preferences).withSelectable(false).withIcon(R.drawable.ic_account_settings).withIconTintingEnabled(true));
listItems.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_SETTINGS).withName(R.string.action_view_preferences).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_settings));
listItems.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_ABOUT).withName(R.string.about_title_activity).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_info));
listItems.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_LOG_OUT).withName(R.string.action_logout).withSelectable(false).withIcon(GoogleMaterial.Icon.gmd_exit_to_app));
listItems.add(new SecondaryDrawerItem().withIdentifier(DRAWER_ITEM_LOG_OUT).withName(R.string.action_logout).withSelectable(false).withIcon(R.drawable.ic_logout).withIconTintingEnabled(true));
drawer = new DrawerBuilder()
.withActivity(this)
@ -344,19 +337,14 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
} else if (drawerItemIdentifier == DRAWER_ITEM_FAVOURITES) {
Intent intent = new Intent(MainActivity.this, FavouritesActivity.class);
startActivityWithSlideInAnimation(intent);
} else if (drawerItemIdentifier == DRAWER_ITEM_MUTED_USERS) {
Intent intent = new Intent(MainActivity.this, AccountListActivity.class);
intent.putExtra("type", AccountListActivity.Type.MUTES);
startActivityWithSlideInAnimation(intent);
} else if (drawerItemIdentifier == DRAWER_ITEM_BLOCKED_USERS) {
Intent intent = new Intent(MainActivity.this, AccountListActivity.class);
intent.putExtra("type", AccountListActivity.Type.BLOCKS);
startActivityWithSlideInAnimation(intent);
} else if (drawerItemIdentifier == DRAWER_ITEM_SEARCH) {
Intent intent = new Intent(MainActivity.this, SearchActivity.class);
startActivityWithSlideInAnimation(intent);
} else if (drawerItemIdentifier == DRAWER_ITEM_PREFERENCES) {
Intent intent = new Intent(MainActivity.this, PreferencesActivity.class);
} else if (drawerItemIdentifier == DRAWER_ITEM_ACCOUNT_SETTINGS) {
Intent intent = PreferencesActivity.newIntent(MainActivity.this, PreferencesActivity.ACCOUNT_PREFERENCES);
startActivityWithSlideInAnimation(intent);
} else if (drawerItemIdentifier == DRAWER_ITEM_SETTINGS) {
Intent intent = PreferencesActivity.newIntent(MainActivity.this, PreferencesActivity.GENERAL_PREFERENCES);
startActivityWithSlideInAnimation(intent);
} else if (drawerItemIdentifier == DRAWER_ITEM_ABOUT) {
Intent intent = new Intent(MainActivity.this, AboutActivity.class);
@ -444,8 +432,9 @@ public final class MainActivity extends BottomSheetActivity implements ActionBut
AccountEntity newAccount = accountManager.logActiveAccountOut();
if (!NotificationHelper.areNotificationsEnabled(MainActivity.this, accountManager))
disablePushNotifications();
if (!NotificationHelper.areNotificationsEnabled(MainActivity.this, accountManager)) {
NotificationHelper.disablePullNotifications();
}
Intent intent;
if (newAccount == null) {

View File

@ -1,180 +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;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.annotation.XmlRes;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import com.keylesspalace.tusky.fragment.PreferencesFragment;
import com.keylesspalace.tusky.util.ThemeUtils;
public class PreferencesActivity extends BaseActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
private boolean restartActivitiesOnExit;
private @XmlRes int currentPreferences;
private @StringRes int currentTitle;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
restartActivitiesOnExit = savedInstanceState.getBoolean("restart");
} else {
Bundle extras = getIntent().getExtras();
restartActivitiesOnExit = extras != null && extras.getBoolean("restart");
}
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
setContentView(R.layout.activity_preferences);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
}
preferences.registerOnSharedPreferenceChangeListener(this);
if (savedInstanceState == null) {
currentPreferences = R.xml.preferences;
currentTitle = R.string.action_view_preferences;
} else {
currentPreferences = savedInstanceState.getInt("preferences");
currentTitle = savedInstanceState.getInt("title");
}
showFragment(currentPreferences, currentTitle);
}
public void showFragment(@XmlRes int preferenceId, @StringRes int title) {
//TODO: cache the Fragments so they can be reused
getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, PreferencesFragment.newInstance(preferenceId))
.commit();
getFragmentManager().executePendingTransactions();
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(title);
}
currentPreferences = preferenceId;
currentTitle = title;
}
private void saveInstanceState(Bundle outState) {
outState.putBoolean("restart", restartActivitiesOnExit);
outState.putInt("preferences", currentPreferences);
outState.putInt("title", currentTitle);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
saveInstanceState(outState);
super.onSaveInstanceState(outState);
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
switch (key) {
case "appTheme": {
String theme = sharedPreferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT);
Log.d("activeTheme", theme);
ThemeUtils.setAppNightMode(theme, this);
restartActivitiesOnExit = true;
// recreate() could be used instead, but it doesn't have an animation B).
Intent intent = getIntent();
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
Bundle savedInstanceState = new Bundle();
saveInstanceState(savedInstanceState);
intent.putExtras(savedInstanceState);
startActivityWithSlideInAnimation(intent);
finish();
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
case "statusTextSize": {
restartActivitiesOnExit = true;
break;
}
case "absoluteTimeView": {
restartActivitiesOnExit = true;
break;
}
case "notificationsEnabled": {
boolean enabled = sharedPreferences.getBoolean("notificationsEnabled", true);
if (enabled) {
enablePushNotifications();
} else {
disablePushNotifications();
}
break;
}
case "pullNotificationCheckInterval": {
String s = sharedPreferences.getString("pullNotificationCheckInterval", "15");
long minutes = Long.valueOf(s);
setPullNotificationCheckInterval(minutes);
break;
}
}
}
@Override
public void onBackPressed() {
//if we are not on the top level, show the top level. Else exit the activity
if (currentPreferences != R.xml.preferences) {
showFragment(R.xml.preferences, R.string.action_view_preferences);
} else {
/* Switching themes won't actually change the theme of activities on the back stack.
* Either the back stack activities need to all be recreated, or do the easier thing, which
* is hijack the back button press and use it to launch a new MainActivity and clear the
* back stack. */
if (restartActivitiesOnExit) {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivityWithSlideInAnimation(intent);
} else {
super.onBackPressed();
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: {
onBackPressed();
return true;
}
}
return super.onOptionsItemSelected(item);
}
}

View File

@ -0,0 +1,186 @@
/* 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
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.preference.PreferenceManager
import android.support.v4.app.Fragment
import android.util.Log
import android.view.MenuItem
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
import com.keylesspalace.tusky.fragment.preference.*
import com.keylesspalace.tusky.util.ThemeUtils
import com.keylesspalace.tusky.util.getNonNullString
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector
import kotlinx.android.synthetic.main.toolbar_basic.*
import java.lang.IllegalArgumentException
import javax.inject.Inject
class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceChangeListener, HasSupportFragmentInjector {
@Inject
lateinit var eventHub: EventHub
@Inject
lateinit var fragmentInjector: DispatchingAndroidInjector<Fragment>
private var restartActivitiesOnExit: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_preferences)
setSupportActionBar(toolbar)
supportActionBar?.run {
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
val fragment: Fragment = when(intent.getIntExtra(EXTRA_PREFERENCE_TYPE, 0)) {
GENERAL_PREFERENCES -> {
setTitle(R.string.action_view_preferences)
PreferencesFragment.newInstance()
}
ACCOUNT_PREFERENCES -> {
setTitle(R.string.action_view_account_preferences)
AccountPreferencesFragment.newInstance()
}
NOTIFICATION_PREFERENCES -> {
setTitle(R.string.pref_title_edit_notification_settings)
NotificationPreferencesFragment.newInstance()
}
TAB_FILTER_PREFERENCES -> {
setTitle(R.string.pref_title_status_tabs)
TabFilterPreferencesFragment.newInstance()
}
PROXY_PREFERENCES -> {
setTitle(R.string.pref_title_http_proxy_settings)
ProxyPreferencesFragment.newInstance()
}
else -> throw IllegalArgumentException("preferenceType not known")
}
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit()
}
override fun onResume() {
super.onResume()
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
onBackPressed()
return true
}
}
return super.onOptionsItemSelected(item)
}
private fun saveInstanceState(outState: Bundle) {
outState.putBoolean("restart", restartActivitiesOnExit)
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putBoolean("restart", restartActivitiesOnExit)
super.onSaveInstanceState(outState)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
when (key) {
"appTheme" -> {
val theme = sharedPreferences.getString("appTheme", ThemeUtils.APP_THEME_DEFAULT)
Log.d("activeTheme", theme)
ThemeUtils.setAppNightMode(theme!!, this)
restartActivitiesOnExit = true
// recreate() could be used instead, but it doesn't have an animation B).
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
val savedInstanceState = Bundle()
saveInstanceState(savedInstanceState)
intent.putExtras(savedInstanceState)
startActivityWithSlideInAnimation(intent)
finish()
overridePendingTransition(R.anim.fade_in, R.anim.fade_out)
restartActivitiesOnExit = true
}
"statusTextSize" -> {
restartActivitiesOnExit = true
}
"absoluteTimeView" -> {
restartActivitiesOnExit = true
}
}
eventHub.dispatch(PreferenceChangedEvent(key))
}
override fun onBackPressed() {
/* Switching themes won't actually change the theme of activities on the back stack.
* Either the back stack activities need to all be recreated, or do the easier thing, which
* is hijack the back button press and use it to launch a new MainActivity and clear the
* back stack. */
if (restartActivitiesOnExit) {
val intent = Intent(this, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivityWithSlideInAnimation(intent)
} else {
super.onBackPressed()
}
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return fragmentInjector
}
companion object {
const val GENERAL_PREFERENCES = 0
const val ACCOUNT_PREFERENCES = 1
const val NOTIFICATION_PREFERENCES = 2
const val TAB_FILTER_PREFERENCES = 3
const val PROXY_PREFERENCES = 4
private const val EXTRA_PREFERENCE_TYPE = "EXTRA_PREFERENCE_TYPE"
@JvmStatic
fun newIntent(context: Context, preferenceType: Int): Intent {
val intent = Intent(context, PreferencesActivity::class.java)
intent.putExtra(EXTRA_PREFERENCE_TYPE, preferenceType)
return intent
}
}
}

View File

@ -20,9 +20,7 @@ import android.app.Application;
import android.app.Service;
import android.arch.persistence.room.Room;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.text.emoji.EmojiCompat;
import com.evernote.android.job.JobManager;
@ -31,6 +29,7 @@ import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.db.AppDatabase;
import com.keylesspalace.tusky.di.AppInjector;
import com.keylesspalace.tusky.util.EmojiCompatFont;
import com.keylesspalace.tusky.util.NotificationPullJobCreator;
import com.squareup.picasso.Picasso;
import javax.inject.Inject;
@ -59,10 +58,6 @@ public class TuskyApplication extends Application implements HasActivityInjector
private ServiceLocator serviceLocator;
public static TuskyApplication getInstance(@NonNull Context context) {
return (TuskyApplication) context.getApplicationContext();
}
@Override
public void onCreate() {
super.onCreate();
@ -70,7 +65,8 @@ public class TuskyApplication extends Application implements HasActivityInjector
appDatabase = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "tuskyDB")
.allowMainThreadQueries()
.addMigrations(AppDatabase.MIGRATION_2_3, AppDatabase.MIGRATION_3_4, AppDatabase.MIGRATION_4_5,
AppDatabase.MIGRATION_5_6, AppDatabase.MIGRATION_6_7, AppDatabase.MIGRATION_7_8, AppDatabase.MIGRATION_8_9)
AppDatabase.MIGRATION_5_6, AppDatabase.MIGRATION_6_7, AppDatabase.MIGRATION_7_8,
AppDatabase.MIGRATION_8_9, AppDatabase.MIGRATION_9_10)
.build();
accountManager = new AccountManager(appDatabase);
serviceLocator = new ServiceLocator() {

View File

@ -10,4 +10,5 @@ data class BlockEvent(val accountId: String) : Dispatchable
data class MuteEvent(val accountId: String) : Dispatchable
data class StatusDeletedEvent(val statusId: String) : Dispatchable
data class StatusComposedEvent(val status: Status) : Dispatchable
data class ProfileEditedEvent(val newProfileData: Account) : Dispatchable
data class ProfileEditedEvent(val newProfileData: Account) : Dispatchable
data class PreferenceChangedEvent(val preferenceKey: String) : Dispatchable

View File

@ -21,6 +21,7 @@ import android.arch.persistence.room.PrimaryKey
import android.arch.persistence.room.TypeConverters
import com.keylesspalace.tusky.entity.Emoji
import com.keylesspalace.tusky.entity.Status
@Entity(indices = [Index(value = ["domain", "accountId"],
unique = true)])
@ -41,6 +42,10 @@ data class AccountEntity(@field:PrimaryKey(autoGenerate = true) var id: Long,
var notificationSound: Boolean = true,
var notificationVibration: Boolean = true,
var notificationLight: Boolean = true,
var defaultPostPrivacy: Status.Visibility = Status.Visibility.PUBLIC,
var defaultMediaSensitivity: Boolean = false,
var alwaysShowSensitiveMedia: Boolean = false,
var mediaPreviewEnabled: Boolean = true,
var lastNotificationId: String = "0",
var activeNotifications: String = "[]",
var emojis: List<Emoji> = emptyList()) {

View File

@ -17,6 +17,7 @@ package com.keylesspalace.tusky.db
import android.util.Log
import com.keylesspalace.tusky.entity.Account
import com.keylesspalace.tusky.entity.Status
/**
* This class caches the account database and handles all account related operations
@ -111,6 +112,8 @@ class AccountManager(db: AppDatabase) {
it.username = account.username
it.displayName = account.name
it.profilePictureUrl = account.avatar
it.defaultPostPrivacy = account.source?.privacy ?: Status.Visibility.PUBLIC
it.defaultMediaSensitivity = account.source?.sensitive ?: false
it.emojis = account.emojis ?: emptyList()
Log.d(TAG, "updateActiveAccount: saving account with id " + it.id)

View File

@ -25,7 +25,7 @@ import android.support.annotation.NonNull;
* DB version & declare DAO
*/
@Database(entities = {TootEntity.class, AccountEntity.class, InstanceEntity.class}, version = 9, exportSchema = false)
@Database(entities = {TootEntity.class, AccountEntity.class, InstanceEntity.class}, version = 10, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract TootDao tootDao();
@ -105,4 +105,15 @@ public abstract class AppDatabase extends RoomDatabase {
database.execSQL("ALTER TABLE `TootEntity` ADD COLUMN `descriptions` TEXT DEFAULT '[]'");
}
};
public static final Migration MIGRATION_9_10 = new Migration(9, 10) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `defaultPostPrivacy` INTEGER NOT NULL DEFAULT 1");
database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `defaultMediaSensitivity` INTEGER NOT NULL DEFAULT 0");
database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `alwaysShowSensitiveMedia` INTEGER NOT NULL DEFAULT 0");
database.execSQL("ALTER TABLE `AccountEntity` ADD COLUMN `mediaPreviewEnabled` INTEGER NOT NULL DEFAULT '1'");
}
};
}

View File

@ -19,6 +19,7 @@ import android.arch.persistence.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.keylesspalace.tusky.entity.Emoji
import com.keylesspalace.tusky.entity.Status
class Converters {
@ -33,4 +34,14 @@ class Converters {
fun emojiListToJson(emojiList: List<Emoji>?): String {
return gson.toJson(emojiList)
}
@TypeConverter
fun visibilityToInt(visibility: Status.Visibility): Int {
return visibility.num
}
@TypeConverter
fun intToVisibility(visibility: Int): Status.Visibility {
return Status.Visibility.byNum(visibility)
}
}

View File

@ -77,8 +77,8 @@ abstract class ActivitiesModule {
@ContributesAndroidInjector
abstract fun contributesSavedTootActivity(): SavedTootActivity
@ContributesAndroidInjector
abstract fun contributesSPreferencesActivity(): PreferencesActivity
@ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contributesPreferencesActivity(): PreferencesActivity
@ContributesAndroidInjector
abstract fun contributesViewMediaActivity(): ViewMediaActivity

View File

@ -17,6 +17,7 @@
package com.keylesspalace.tusky.di
import com.keylesspalace.tusky.fragment.*
import com.keylesspalace.tusky.fragment.preference.*
import dagger.Module
import dagger.android.ContributesAndroidInjector
@ -43,4 +44,11 @@ abstract class FragmentBuildersModule {
@ContributesAndroidInjector
abstract fun searchFragment(): SearchFragment
@ContributesAndroidInjector
abstract fun notificationPreferencesFragment(): NotificationPreferencesFragment
@ContributesAndroidInjector
abstract fun accountPreferencesFragment(): AccountPreferencesFragment
}

View File

@ -47,6 +47,7 @@ import com.keylesspalace.tusky.adapter.NotificationsAdapter;
import com.keylesspalace.tusky.appstore.BlockEvent;
import com.keylesspalace.tusky.appstore.EventHub;
import com.keylesspalace.tusky.appstore.FavoriteEvent;
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent;
import com.keylesspalace.tusky.appstore.ReblogEvent;
import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
@ -87,7 +88,6 @@ public class NotificationsFragment extends SFragment implements
SwipeRefreshLayout.OnRefreshListener,
StatusActionListener,
NotificationsAdapter.NotificationActionListener,
SharedPreferences.OnSharedPreferenceChangeListener,
Injectable {
private static final String TAG = "NotificationF"; // logging tag
@ -194,10 +194,9 @@ public class NotificationsFragment extends SFragment implements
recyclerView.addItemDecoration(divider);
adapter = new NotificationsAdapter(this, this);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(
getActivity());
alwaysShowSensitiveMedia = preferences.getBoolean("alwaysShowSensitiveMedia", false);
boolean mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
alwaysShowSensitiveMedia = accountManager.getActiveAccount().getAlwaysShowSensitiveMedia();
boolean mediaPreviewEnabled = accountManager.getActiveAccount().getMediaPreviewEnabled();
adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
boolean useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false);
adapter.setUseAbsoluteTime(useAbsoluteTime);
@ -273,7 +272,6 @@ public class NotificationsFragment extends SFragment implements
* Use a modified scroll listener that both loads more notificationsEnabled as it goes, and hides
* the compose button on down-scroll. */
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
preferences.registerOnSharedPreferenceChangeListener(this);
hideFab = preferences.getBoolean("fabHide", false);
scrollListener = new EndlessOnScrollListener(layoutManager) {
@Override
@ -314,6 +312,8 @@ public class NotificationsFragment extends SFragment implements
handleReblogEvent((ReblogEvent) event);
} else if (event instanceof BlockEvent) {
removeAllByAccountId(((BlockEvent) event).getAccountId());
} else if (event instanceof PreferenceChangedEvent) {
onPreferenceChanged(((PreferenceChangedEvent) event).getPreferenceKey());
}
});
}
@ -564,15 +564,14 @@ public class NotificationsFragment extends SFragment implements
Log.w(TAG, "Didn't find a notification for ID: " + notificationId);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
public void onPreferenceChanged(String key) {
switch (key) {
case "fabHide": {
hideFab = sharedPreferences.getBoolean("fabHide", false);
hideFab = PreferenceManager.getDefaultSharedPreferences(getContext()).getBoolean("fabHide", false);
break;
}
case "mediaPreviewEnabled": {
boolean enabled = sharedPreferences.getBoolean("mediaPreviewEnabled", true);
boolean enabled = accountManager.getActiveAccount().getMediaPreviewEnabled();
if (enabled != adapter.isMediaPreviewEnabled()) {
adapter.setMediaPreviewEnabled(enabled);
fullyRefresh();

View File

@ -1,303 +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.app.AlertDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.support.annotation.XmlRes;
import android.text.Editable;
import android.text.TextWatcher;
import com.keylesspalace.tusky.BuildConfig;
import com.keylesspalace.tusky.PreferencesActivity;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.TuskyApplication;
import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import java.util.regex.Pattern;
public class PreferencesFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
SharedPreferences sharedPreferences;
static boolean httpProxyChanged = false;
static boolean pendingRestart = false;
public static PreferencesFragment newInstance(@XmlRes int preference) {
PreferencesFragment fragment = new PreferencesFragment();
Bundle args = new Bundle();
args.putInt("preference", preference);
fragment.setArguments(args);
return fragment;
}
private AccountManager accountManager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
accountManager = TuskyApplication.getInstance(getActivity()).getServiceLocator()
.get(AccountManager.class);
int preference = getArguments().getInt("preference");
addPreferencesFromResource(preference);
Preference regexPref = findPreference("tabFilterRegex");
if (regexPref != null) regexPref.setOnPreferenceClickListener(pref -> {
// Reset the error dialog when shown; if the dialog was closed with the cancel button
// while an invalid regex was present, this would otherwise cause buggy behaviour.
((EditTextPreference) regexPref).getEditText().setError(null);
// Test the regex as the user inputs text, ensuring immediate feedback and preventing
// setting of an invalid regex, which would cause a crash loop.
((EditTextPreference) regexPref).getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
try {
Pattern.compile(s.toString());
((EditTextPreference) regexPref).getEditText().setError(null);
AlertDialog dialog = (AlertDialog) ((EditTextPreference) pref).getDialog();
if (dialog != null) dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
} catch (IllegalArgumentException e) {
((AlertDialog) ((EditTextPreference) pref).getDialog()).getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
((EditTextPreference) regexPref).getEditText().setError(getString(R.string.error_invalid_regex));
}
}
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
return false;
});
Preference notificationPreferences = findPreference("notificationPreferences");
if (notificationPreferences != null) {
AccountEntity activeAccount = accountManager.getActiveAccount();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O && activeAccount != null) {
notificationPreferences.setSummary(getString(R.string.pref_summary_notifications, activeAccount.getFullName()));
}
//on Android O and newer, launch the system notification settings instead of the app settings
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationPreferences.setOnPreferenceClickListener(pref -> {
Intent intent = new Intent();
intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
intent.putExtra("android.provider.extra.APP_PACKAGE", BuildConfig.APPLICATION_ID);
startActivity(intent);
return true;
});
} else {
notificationPreferences.setOnPreferenceClickListener(pref -> {
PreferencesActivity activity = (PreferencesActivity) getActivity();
if (activity != null) {
activity.showFragment(R.xml.notification_preferences, R.string.pref_title_edit_notification_settings);
}
return true;
});
}
}
Preference timelineFilterPreferences = findPreference("timelineFilterPreferences");
if (timelineFilterPreferences != null) {
timelineFilterPreferences.setOnPreferenceClickListener(pref -> {
PreferencesActivity activity = (PreferencesActivity) getActivity();
if (activity != null) {
activity.showFragment(R.xml.timeline_filter_preferences, R.string.pref_title_status_tabs);
}
return true;
});
}
Preference httpProxyPreferences = findPreference("httpProxyPreferences");
if (httpProxyPreferences != null) {
httpProxyPreferences.setOnPreferenceClickListener(pref -> {
PreferencesActivity activity = (PreferencesActivity) getActivity();
if (activity != null) {
pendingRestart = false;
activity.showFragment(R.xml.http_proxy_preferences, R.string.pref_title_http_proxy_settings);
}
return true;
});
}
if (preference == R.xml.notification_preferences) {
AccountEntity activeAccount = accountManager.getActiveAccount();
if (activeAccount != null) {
CheckBoxPreference notificationPref = (CheckBoxPreference) findPreference("notificationsEnabled");
notificationPref.setChecked(activeAccount.getNotificationsEnabled());
CheckBoxPreference mentionedPref = (CheckBoxPreference) findPreference("notificationFilterMentions");
mentionedPref.setChecked(activeAccount.getNotificationsMentioned());
CheckBoxPreference followedPref = (CheckBoxPreference) findPreference("notificationFilterFollows");
followedPref.setChecked(activeAccount.getNotificationsFollowed());
CheckBoxPreference boostedPref = (CheckBoxPreference) findPreference("notificationFilterReblogs");
boostedPref.setChecked(activeAccount.getNotificationsReblogged());
CheckBoxPreference favoritedPref = (CheckBoxPreference) findPreference("notificationFilterFavourites");
favoritedPref.setChecked(activeAccount.getNotificationsFavorited());
CheckBoxPreference soundPref = (CheckBoxPreference) findPreference("notificationAlertSound");
soundPref.setChecked(activeAccount.getNotificationSound());
CheckBoxPreference vibrationPref = (CheckBoxPreference) findPreference("notificationAlertVibrate");
vibrationPref.setChecked(activeAccount.getNotificationVibration());
CheckBoxPreference lightPref = (CheckBoxPreference) findPreference("notificationAlertLight");
lightPref.setChecked(activeAccount.getNotificationLight());
}
}
}
@Override
public void onResume() {
super.onResume();
sharedPreferences = getPreferenceManager().getSharedPreferences();
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
updateSummary("httpProxyServer");
updateSummary("httpProxyPort");
updateHttpProxySummary();
}
@Override
public void onPause() {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this);
super.onPause();
if (pendingRestart) {
pendingRestart = false;
httpProxyChanged = false;
System.exit(0);
}
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
switch (key) {
case "httpProxyServer":
case "httpProxyPort":
updateSummary(key);
case "httpProxyEnabled":
httpProxyChanged = true;
return;
default:
}
AccountEntity activeAccount = accountManager.getActiveAccount();
if (activeAccount != null) {
switch (key) {
case "notificationsEnabled":
activeAccount.setNotificationsEnabled(sharedPreferences.getBoolean(key, true));
break;
case "notificationFilterMentions":
activeAccount.setNotificationsMentioned(sharedPreferences.getBoolean(key, true));
break;
case "notificationFilterFollows":
activeAccount.setNotificationsFollowed(sharedPreferences.getBoolean(key, true));
break;
case "notificationFilterReblogs":
activeAccount.setNotificationsReblogged(sharedPreferences.getBoolean(key, true));
break;
case "notificationFilterFavourites":
activeAccount.setNotificationsFavorited(sharedPreferences.getBoolean(key, true));
break;
case "notificationAlertSound":
activeAccount.setNotificationSound(sharedPreferences.getBoolean(key, true));
break;
case "notificationAlertVibrate":
activeAccount.setNotificationVibration(sharedPreferences.getBoolean(key, true));
break;
case "notificationAlertLight":
activeAccount.setNotificationLight(sharedPreferences.getBoolean(key, true));
break;
}
accountManager.saveAccount(activeAccount);
}
}
private void updateSummary(String key) {
switch (key) {
case "httpProxyServer":
case "httpProxyPort":
EditTextPreference editTextPreference = (EditTextPreference) findPreference(key);
if (editTextPreference != null) {
editTextPreference.setSummary(editTextPreference.getText());
}
break;
default:
}
}
private void updateHttpProxySummary() {
Preference httpProxyPref = findPreference("httpProxyPreferences");
if (httpProxyPref != null) {
if (httpProxyChanged) {
pendingRestart = true;
}
Boolean httpProxyEnabled = sharedPreferences.getBoolean("httpProxyEnabled", false);
String httpServer = sharedPreferences.getString("httpProxyServer", "");
try {
int httpPort = Integer.parseInt(sharedPreferences.getString("httpProxyPort", "-1"));
if (httpProxyEnabled && !httpServer.isEmpty() && (httpPort > 0 && httpPort < 65535)) {
httpProxyPref.setSummary(httpServer + ":" + httpPort);
return;
}
} catch (NumberFormatException e) {
// user has entered wrong port, fall back to empty summary
}
httpProxyPref.setSummary("");
}
}
}

View File

@ -50,10 +50,12 @@ import com.keylesspalace.tusky.appstore.BlockEvent;
import com.keylesspalace.tusky.appstore.EventHub;
import com.keylesspalace.tusky.appstore.FavoriteEvent;
import com.keylesspalace.tusky.appstore.MuteEvent;
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent;
import com.keylesspalace.tusky.appstore.ReblogEvent;
import com.keylesspalace.tusky.appstore.StatusComposedEvent;
import com.keylesspalace.tusky.appstore.StatusDeletedEvent;
import com.keylesspalace.tusky.appstore.UnfollowEvent;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.di.Injectable;
import com.keylesspalace.tusky.entity.Status;
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
@ -90,7 +92,6 @@ import static com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvid
public class TimelineFragment extends SFragment implements
SwipeRefreshLayout.OnRefreshListener,
StatusActionListener,
SharedPreferences.OnSharedPreferenceChangeListener,
Injectable {
private static final String TAG = "TimelineF"; // logging tag
private static final String KIND_ARG = "kind";
@ -119,6 +120,9 @@ public class TimelineFragment extends SFragment implements
public TimelineCases timelineCases;
@Inject
public EventHub eventHub;
@Inject
public AccountManager accountManager;
private boolean eventRegistered = false;
private SwipeRefreshLayout swipeRefreshLayout;
@ -244,9 +248,8 @@ public class TimelineFragment extends SFragment implements
private void setupTimelinePreferences() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
preferences.registerOnSharedPreferenceChangeListener(this);
alwaysShowSensitiveMedia = preferences.getBoolean("alwaysShowSensitiveMedia", false);
boolean mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true);
alwaysShowSensitiveMedia = accountManager.getActiveAccount().getAlwaysShowSensitiveMedia();
boolean mediaPreviewEnabled = accountManager.getActiveAccount().getMediaPreviewEnabled();
adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
boolean useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false);
adapter.setUseAbsoluteTime(useAbsoluteTime);
@ -413,6 +416,8 @@ public class TimelineFragment extends SFragment implements
} else if (event instanceof StatusComposedEvent) {
Status status = ((StatusComposedEvent) event).getStatus();
handleStatusComposeEvent(status);
} else if (event instanceof PreferenceChangedEvent) {
onPreferenceChanged(((PreferenceChangedEvent) event).getPreferenceKey());
}
});
eventRegistered = true;
@ -630,15 +635,15 @@ public class TimelineFragment extends SFragment implements
super.viewAccount(id);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
private void onPreferenceChanged(String key) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
switch (key) {
case "fabHide": {
hideFab = sharedPreferences.getBoolean("fabHide", false);
break;
}
case "mediaPreviewEnabled": {
boolean enabled = sharedPreferences.getBoolean("mediaPreviewEnabled", true);
boolean enabled = accountManager.getActiveAccount().getMediaPreviewEnabled();
boolean oldMediaPreviewEnabled = adapter.getMediaPreviewEnabled();
if (enabled != oldMediaPreviewEnabled) {
adapter.setMediaPreviewEnabled(enabled);
@ -682,7 +687,7 @@ public class TimelineFragment extends SFragment implements
}
case "alwaysShowSensitiveMedia": {
//it is ok if only newly loaded statuses are affected, no need to fully refresh
alwaysShowSensitiveMedia = sharedPreferences.getBoolean("alwaysShowSensitiveMedia", false);
alwaysShowSensitiveMedia = accountManager.getActiveAccount().getAlwaysShowSensitiveMedia();
break;
}
}

View File

@ -157,8 +157,8 @@ public final class ViewThreadFragment extends SFragment implements
threadLineDrawable));
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(
getActivity());
alwaysShowSensitiveMedia = preferences.getBoolean("alwaysShowSensitiveMedia", false);
boolean mediaPreviewEnabled = preferences.getBoolean("mediaPreviewEnabled", true);
alwaysShowSensitiveMedia = accountManager.getActiveAccount().getAlwaysShowSensitiveMedia();
boolean mediaPreviewEnabled = accountManager.getActiveAccount().getMediaPreviewEnabled();
adapter.setMediaPreviewEnabled(mediaPreviewEnabled);
boolean useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false);
adapter.setUseAbsoluteTime(useAbsoluteTime);

View File

@ -0,0 +1,252 @@
/* 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.fragment.preference
import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.support.design.widget.Snackbar
import android.support.v14.preference.SwitchPreference
import android.support.v7.preference.ListPreference
import android.support.v7.preference.Preference
import android.support.v7.preference.PreferenceFragmentCompat
import android.util.Log
import android.view.View
import com.keylesspalace.tusky.AccountListActivity
import com.keylesspalace.tusky.BuildConfig
import com.keylesspalace.tusky.PreferencesActivity
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.entity.Account
import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.ThemeUtils
import com.mikepenz.google_material_typeface_library.GoogleMaterial
import com.mikepenz.iconics.IconicsDrawable
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import javax.inject.Inject
class AccountPreferencesFragment : PreferenceFragmentCompat(),
Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
Injectable {
@Inject
lateinit var accountManager: AccountManager
@Inject
lateinit var mastodonApi: MastodonApi
@Inject
lateinit var eventHub: EventHub
private lateinit var notificationPreference: Preference
private lateinit var mutedUsersPreference: Preference
private lateinit var blockedUsersPreference: Preference
private lateinit var defaultPostPrivacyPreference: ListPreference
private lateinit var defaultMediaSensitivityPreference: SwitchPreference
private lateinit var alwaysShowSensitiveMediaPreference: SwitchPreference
private lateinit var mediaPreviewEnabledPreference: SwitchPreference
private val iconSize by lazy {resources.getDimensionPixelSize(R.dimen.preference_icon_size)}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.account_preferences)
notificationPreference = findPreference("notificationPreference")
mutedUsersPreference = findPreference("mutedUsersPreference")
blockedUsersPreference = findPreference("blockedUsersPreference")
defaultPostPrivacyPreference = findPreference("defaultPostPrivacy") as ListPreference
defaultMediaSensitivityPreference = findPreference("defaultMediaSensitivity") as SwitchPreference
mediaPreviewEnabledPreference = findPreference("mediaPreviewEnabled") as SwitchPreference
alwaysShowSensitiveMediaPreference = findPreference("alwaysShowSensitiveMedia") as SwitchPreference
notificationPreference.icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_notifications).sizePx(iconSize).color(ThemeUtils.getColor(context, R.attr.toolbar_icon_tint))
mutedUsersPreference.icon = getTintedIcon(R.drawable.ic_mute_24dp)
blockedUsersPreference.icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_block).sizePx(iconSize).color(ThemeUtils.getColor(context, R.attr.toolbar_icon_tint))
notificationPreference.onPreferenceClickListener = this
mutedUsersPreference.onPreferenceClickListener = this
blockedUsersPreference.onPreferenceClickListener = this
defaultPostPrivacyPreference.onPreferenceChangeListener = this
defaultMediaSensitivityPreference.onPreferenceChangeListener = this
mediaPreviewEnabledPreference.onPreferenceChangeListener = this
alwaysShowSensitiveMediaPreference.onPreferenceChangeListener = this
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
accountManager.activeAccount?.let {
defaultPostPrivacyPreference.value = it.defaultPostPrivacy.serverString()
defaultPostPrivacyPreference.icon = getIconForVisibility(it.defaultPostPrivacy)
defaultMediaSensitivityPreference.isChecked = it.defaultMediaSensitivity
defaultMediaSensitivityPreference.icon = getIconForSensitivity(it.defaultMediaSensitivity)
mediaPreviewEnabledPreference.isChecked = it.mediaPreviewEnabled
alwaysShowSensitiveMediaPreference.isChecked = it.alwaysShowSensitiveMedia
}
}
override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
when(preference) {
defaultPostPrivacyPreference -> {
preference.icon = getIconForVisibility(Status.Visibility.byString(newValue as String))
syncWithServer(visibility = newValue)
}
defaultMediaSensitivityPreference -> {
preference.icon = getIconForSensitivity(newValue as Boolean)
syncWithServer(sensitive = newValue)
}
mediaPreviewEnabledPreference -> {
accountManager.activeAccount?.let {
it.mediaPreviewEnabled = newValue as Boolean
accountManager.saveAccount(it)
}
}
alwaysShowSensitiveMediaPreference -> {
accountManager.activeAccount?.let {
it.alwaysShowSensitiveMedia = newValue as Boolean
accountManager.saveAccount(it)
}
}
}
eventHub.dispatch(PreferenceChangedEvent(preference.key))
return true
}
override fun onPreferenceClick(preference: Preference): Boolean {
when(preference) {
notificationPreference -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val intent = Intent()
intent.action = "android.settings.APP_NOTIFICATION_SETTINGS"
intent.putExtra("android.provider.extra.APP_PACKAGE", BuildConfig.APPLICATION_ID)
startActivity(intent)
} else {
activity?.let {
val intent = PreferencesActivity.newIntent(it, PreferencesActivity.NOTIFICATION_PREFERENCES)
it.startActivity(intent)
it.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
}
}
return true
}
mutedUsersPreference -> {
val intent = Intent(context, AccountListActivity::class.java)
intent.putExtra("type", AccountListActivity.Type.MUTES)
activity?.startActivity(intent)
activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
return true
}
blockedUsersPreference -> {
val intent = Intent(context, AccountListActivity::class.java)
intent.putExtra("type", AccountListActivity.Type.BLOCKS)
activity?.startActivity(intent)
activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
return true
}
else -> return false
}
}
private fun syncWithServer(visibility: String? = null, sensitive: Boolean? = null) {
mastodonApi.accountUpdateSource(visibility, sensitive)
.enqueue(object: Callback<Account>{
override fun onResponse(call: Call<Account>, response: Response<Account>) {
val account = response.body()
if(response.isSuccessful && account != null) {
accountManager.activeAccount?.let {
it.defaultPostPrivacy = account.source?.privacy ?: Status.Visibility.PUBLIC
it.defaultMediaSensitivity = account.source?.sensitive ?: false
accountManager.saveAccount(it)
}
} else {
Log.e("AccountPreferences", "failed updating settings on server")
showErrorSnackbar(visibility, sensitive)
}
}
override fun onFailure(call: Call<Account>, t: Throwable) {
Log.e("AccountPreferences", "failed updating settings on server", t)
showErrorSnackbar(visibility, sensitive)
}
})
}
private fun showErrorSnackbar(visibility: String?, sensitive: Boolean?) {
view?.let {view ->
Snackbar.make(view, R.string.pref_failed_to_sync, Snackbar.LENGTH_LONG)
.setAction(R.string.action_retry) { syncWithServer( visibility, sensitive)}
.show()
}
}
private fun getIconForVisibility(visibility: Status.Visibility): Drawable? {
val drawableId = when (visibility) {
Status.Visibility.PRIVATE -> R.drawable.ic_lock_outline_24dp
Status.Visibility.UNLISTED -> R.drawable.ic_lock_open_24dp
else -> R.drawable.ic_public_24dp
}
return getTintedIcon(drawableId)
}
private fun getIconForSensitivity(sensitive: Boolean): Drawable? {
val drawableId = if (sensitive) {
R.drawable.ic_hide_media_24dp
} else {
R.drawable.ic_eye_24dp
}
return getTintedIcon(drawableId)
}
private fun getTintedIcon(iconId: Int): Drawable? {
val drawable = context?.getDrawable(iconId)
ThemeUtils.setDrawableTint(context, drawable, R.attr.toolbar_icon_tint)
return drawable
}
companion object {
fun newInstance(): AccountPreferencesFragment {
return AccountPreferencesFragment()
}
}
}

View File

@ -0,0 +1,114 @@
/* 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.fragment.preference
import android.os.Bundle
import android.support.v14.preference.SwitchPreference
import android.support.v7.preference.Preference
import android.support.v7.preference.PreferenceFragmentCompat
import android.view.View
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.util.NotificationHelper
import javax.inject.Inject
class NotificationPreferencesFragment : PreferenceFragmentCompat(), Preference.OnPreferenceChangeListener, Injectable {
@Inject
lateinit var accountManager: AccountManager
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.notification_preferences)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val activeAccount = accountManager.activeAccount
if (activeAccount != null) {
val notificationPref = findPreference("notificationsEnabled") as SwitchPreference
notificationPref.isChecked = activeAccount.notificationsEnabled
notificationPref.onPreferenceChangeListener = this
val mentionedPref = findPreference("notificationFilterMentions") as SwitchPreference
mentionedPref.isChecked = activeAccount.notificationsMentioned
mentionedPref.onPreferenceChangeListener = this
val followedPref = findPreference("notificationFilterFollows") as SwitchPreference
followedPref.isChecked = activeAccount.notificationsFollowed
followedPref.onPreferenceChangeListener = this
val boostedPref = findPreference("notificationFilterReblogs") as SwitchPreference
boostedPref.isChecked = activeAccount.notificationsReblogged
boostedPref.onPreferenceChangeListener = this
val favoritedPref = findPreference("notificationFilterFavourites") as SwitchPreference
favoritedPref.isChecked = activeAccount.notificationsFavorited
favoritedPref.onPreferenceChangeListener = this
val soundPref = findPreference("notificationAlertSound") as SwitchPreference
soundPref.isChecked = activeAccount.notificationSound
soundPref.onPreferenceChangeListener = this
val vibrationPref = findPreference("notificationAlertVibrate") as SwitchPreference
vibrationPref.isChecked = activeAccount.notificationVibration
vibrationPref.onPreferenceChangeListener = this
val lightPref = findPreference("notificationAlertLight") as SwitchPreference
lightPref.isChecked = activeAccount.notificationLight
lightPref.onPreferenceChangeListener = this
}
}
override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
val activeAccount = accountManager.activeAccount
if (activeAccount != null) {
when (preference.key) {
"notificationsEnabled" -> {
activeAccount.notificationsEnabled = newValue as Boolean
if(NotificationHelper.areNotificationsEnabled(preference.context, accountManager)) {
NotificationHelper.enablePullNotifications()
} else {
NotificationHelper.disablePullNotifications()
}
}
"notificationFilterMentions" -> activeAccount.notificationsMentioned = newValue as Boolean
"notificationFilterFollows" -> activeAccount.notificationsFollowed = newValue as Boolean
"notificationFilterReblogs" -> activeAccount.notificationsReblogged = newValue as Boolean
"notificationFilterFavourites" -> activeAccount.notificationsFavorited = newValue as Boolean
"notificationAlertSound" -> activeAccount.notificationSound = newValue as Boolean
"notificationAlertVibrate" -> activeAccount.notificationVibration = newValue as Boolean
"notificationAlertLight" -> activeAccount.notificationLight = newValue as Boolean
}
accountManager.saveAccount(activeAccount)
return true
}
return false
}
companion object {
fun newInstance(): NotificationPreferencesFragment {
return NotificationPreferencesFragment()
}
}
}

View File

@ -0,0 +1,101 @@
/* 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.fragment.preference
import android.os.Bundle
import android.support.v7.preference.PreferenceFragmentCompat
import com.keylesspalace.tusky.PreferencesActivity
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.util.ThemeUtils
import com.keylesspalace.tusky.util.getNonNullString
import com.mikepenz.google_material_typeface_library.GoogleMaterial
import com.mikepenz.iconics.IconicsDrawable
class PreferencesFragment : PreferenceFragmentCompat() {
private val iconSize by lazy {resources.getDimensionPixelSize(R.dimen.preference_icon_size)}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences)
val themePreference = findPreference("appTheme")
themePreference.icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_palette).sizePx(iconSize).color(ThemeUtils.getColor(context, R.attr.toolbar_icon_tint))
val emojiPreference = findPreference("emojiCompat")
emojiPreference.icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_sentiment_satisfied).sizePx(iconSize).color(ThemeUtils.getColor(context, R.attr.toolbar_icon_tint))
val textSizePreference = findPreference("statusTextSize")
textSizePreference.icon = IconicsDrawable(context, GoogleMaterial.Icon.gmd_format_size).sizePx(iconSize).color(ThemeUtils.getColor(context, R.attr.toolbar_icon_tint))
val timelineFilterPreferences = findPreference("timelineFilterPreferences")
timelineFilterPreferences.setOnPreferenceClickListener { _ ->
activity?.let {
val intent = PreferencesActivity.newIntent(it, PreferencesActivity.TAB_FILTER_PREFERENCES)
it.startActivity(intent)
it.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
}
true
}
val httpProxyPreferences = findPreference("httpProxyPreferences")
httpProxyPreferences.setOnPreferenceClickListener { _ ->
activity?.let {
val intent = PreferencesActivity.newIntent(it, PreferencesActivity.PROXY_PREFERENCES)
it.startActivity(intent)
it.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left)
}
true
}
}
override fun onResume() {
super.onResume()
updateHttpProxySummary()
}
private fun updateHttpProxySummary() {
val httpProxyPref = findPreference("httpProxyPreferences")
val sharedPreferences = preferenceManager.sharedPreferences
val httpProxyEnabled = sharedPreferences.getBoolean("httpProxyEnabled", false)
val httpServer = sharedPreferences.getNonNullString("httpProxyServer", "")
try {
val httpPort = sharedPreferences.getNonNullString("httpProxyPort", "-1").toInt()
if (httpProxyEnabled && httpServer.isNotBlank() && httpPort > 0 && httpPort < 65535) {
httpProxyPref.summary = "$httpServer:$httpPort"
return
}
} catch (e: NumberFormatException) {
// user has entered wrong port, fall back to empty summary
}
httpProxyPref.summary = ""
}
companion object {
fun newInstance(): PreferencesFragment {
return PreferencesFragment()
}
}
}

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.fragment.preference
import android.content.SharedPreferences
import android.os.Bundle
import android.support.v7.preference.EditTextPreference
import android.support.v7.preference.PreferenceFragmentCompat
import com.keylesspalace.tusky.R
class ProxyPreferencesFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener {
private var pendingRestart = false
private lateinit var sharedPreferences: SharedPreferences
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.http_proxy_preferences)
sharedPreferences = preferenceManager.sharedPreferences
}
override fun onResume() {
super.onResume()
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
updateSummary("httpProxyServer")
updateSummary("httpProxyPort")
}
override fun onPause() {
super.onPause()
sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
if (pendingRestart) {
pendingRestart = false
System.exit(0)
}
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
updateSummary (key)
}
private fun updateSummary(key: String) {
when (key) {
"httpProxyServer", "httpProxyPort" -> {
val editTextPreference = findPreference(key) as EditTextPreference
editTextPreference.summary = editTextPreference.text
}
}
}
companion object {
fun newInstance(): ProxyPreferencesFragment {
return ProxyPreferencesFragment()
}
}
}

View File

@ -0,0 +1,88 @@
/* 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.fragment.preference
import android.content.SharedPreferences
import android.os.Bundle
import android.support.v7.app.AlertDialog
import android.support.v7.preference.PreferenceFragmentCompat
import android.text.Editable
import android.text.TextWatcher
import android.widget.EditText
import com.keylesspalace.tusky.R
import java.util.regex.Pattern
class TabFilterPreferencesFragment : PreferenceFragmentCompat() {
private lateinit var sharedPreferences: SharedPreferences
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.timeline_filter_preferences)
sharedPreferences = preferenceManager.sharedPreferences
val regexPref = findPreference("tabFilterRegex")
if (regexPref != null) {
regexPref.summary = sharedPreferences.getString("tabFilterRegex", "")
regexPref.setOnPreferenceClickListener {
val editText = EditText(requireContext())
editText.setText(sharedPreferences.getString("tabFilterRegex", ""))
val dialog = AlertDialog.Builder(requireContext())
.setTitle(R.string.pref_title_filter_regex)
.setView(editText)
.setPositiveButton(android.R.string.ok) { _, _ ->
sharedPreferences
.edit()
.putString("tabFilterRegex", editText.text.toString())
.apply()
regexPref.summary = editText.text.toString()
}
.setNegativeButton(android.R.string.cancel, null)
.create()
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(newRegex: Editable) {
try {
Pattern.compile(newRegex.toString())
editText.error = null
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = true
} catch (e: IllegalArgumentException) {
editText.error = getString(R.string.error_invalid_regex)
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false
}
}
override fun beforeTextChanged(s1: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s1: CharSequence, start: Int, before: Int, count: Int) {}
})
dialog.show()
true
}
}
}
companion object {
fun newInstance(): TabFilterPreferencesFragment {
return TabFilterPreferencesFragment()
}
}
}

View File

@ -166,6 +166,11 @@ public interface MastodonApi {
@GET("api/v1/accounts/verify_credentials")
Call<Account> accountVerifyCredentials();
@FormUrlEncoded
@PATCH("api/v1/accounts/update_credentials")
Call<Account> accountUpdateSource(@Nullable @Field("source[privacy]") String privacy,
@Nullable @Field("source[sensitive]") Boolean sensitive);
@Multipart
@PATCH("api/v1/accounts/update_credentials")
Call<Account> accountUpdateCredentials(

View File

@ -37,6 +37,8 @@ import android.support.v4.content.ContextCompat;
import android.support.v4.text.BidiFormatter;
import android.util.Log;
import com.evernote.android.job.JobManager;
import com.evernote.android.job.JobRequest;
import com.keylesspalace.tusky.BuildConfig;
import com.keylesspalace.tusky.MainActivity;
import com.keylesspalace.tusky.R;
@ -103,6 +105,13 @@ public class NotificationHelper {
public static final String CHANNEL_BOOST = "CHANNEL_BOOST";
public static final String CHANNEL_FAVOURITE = "CHANNEL_FAVOURITE";
/**
* time in minutes between notification checks
* note that this cannot be less than 15 minutes due to Android battery saving constraints
*/
private static final int NOTIFICATION_CHECK_INTERVAL_MINUTES = 15;
/**
* Takes a given Mastodon notification and either creates a new Android notification or updates
* the state of the existing notification to reflect the new interaction.
@ -424,6 +433,25 @@ public class NotificationHelper {
}
public static void enablePullNotifications() {
long checkInterval = 1000 * 60 * NOTIFICATION_CHECK_INTERVAL_MINUTES;
new JobRequest.Builder(NotificationPullJobCreator.NOTIFICATIONS_JOB_TAG)
.setPeriodic(checkInterval)
.setUpdateCurrent(true)
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.build()
.scheduleAsync();
Log.d(TAG, "enabled notification checks with "+ NOTIFICATION_CHECK_INTERVAL_MINUTES + "min interval");
}
public static void disablePullNotifications() {
JobManager.instance().cancelAllForTag(NotificationPullJobCreator.NOTIFICATIONS_JOB_TAG);
Log.d(TAG, "disabled notification checks");
}
public static void clearNotificationsForActiveAccount(@NonNull Context context, @NonNull AccountManager accountManager) {
AccountEntity account = accountManager.getActiveAccount();
if (account != null) {

View File

@ -13,7 +13,7 @@
* You should have received a copy of the GNU Lesser General Public License along with Tusky. If
* not, see <http://www.gnu.org/licenses/>. */
package com.keylesspalace.tusky;
package com.keylesspalace.tusky.util;
import android.content.Context;
import android.support.annotation.NonNull;
@ -26,7 +26,6 @@ import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.entity.Notification;
import com.keylesspalace.tusky.network.MastodonApi;
import com.keylesspalace.tusky.util.NotificationHelper;
import java.io.IOException;
import java.math.BigInteger;
@ -98,10 +97,10 @@ public final class NotificationPullJobCreator implements JobCreator {
if (notifications.isSuccessful()) {
onNotificationsReceived(account, notifications.body());
} else {
Log.w(TAG, "error receiving notificationsEnabled");
Log.w(TAG, "error receiving notifications");
}
} catch (IOException e) {
Log.w(TAG, "error receiving notificationsEnabled", e);
Log.w(TAG, "error receiving notifications", e);
}
}

View File

@ -0,0 +1,7 @@
package com.keylesspalace.tusky.util
import android.content.SharedPreferences
fun SharedPreferences.getNonNullString(key: String, defValue: String): String {
return this.getString(key, defValue) ?: defValue
}

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillAlpha="1"
android:fillColor="#000000"
android:pathData="M8.9987,3.9987C6.7925,3.9987 5,5.7913 5,7.9975C5,10.21 6.7925,12.0025 8.9987,12.0025C11.2113,12.0025 12.9975,10.21 12.9975,7.9975C12.9975,5.7913 11.2113,3.9987 8.9987,3.9987ZM8.9987,13.9987C6.3288,13.9987 1.0013,15.3325 1.0013,17.9975L1.0013,20L12.08,20C12.0262,19.6675 12.0025,19.3362 12.0025,18.9988C12.0025,17.485 12.495,16.0113 13.4087,14.8C11.88,14.2775 10.1813,13.9987 8.9987,13.9987ZM17.9887,13.9987C17.8712,13.9987 17.7588,14.0912 17.7388,14.2087L17.5487,15.5275C17.2512,15.6588 16.9575,15.82 16.6988,16.02L15.4587,15.5175C15.3512,15.4787 15.22,15.5225 15.1512,15.63L14.15,17.3587C14.0913,17.4713 14.1112,17.5975 14.2087,17.6812L15.2688,18.5113C15.2487,18.6712 15.2388,18.8275 15.2388,18.9988C15.2388,19.17 15.2487,19.3312 15.2688,19.4925L14.2087,20.3225C14.1212,20.4 14.0913,20.5325 14.15,20.64L15.1512,22.3687C15.21,22.48 15.3413,22.52 15.4587,22.48L16.6988,21.9825C16.9575,22.1825 17.2412,22.3488 17.5487,22.4713L17.7388,23.7888C17.7588,23.9113 17.8612,23.9988 17.9887,23.9988L19.99,23.9988C20.1123,23.9988 20.22,23.9113 20.2393,23.7888L20.4297,22.4713C20.7275,22.3387 21.0205,22.1825 21.27,21.9825L22.52,22.48C22.6312,22.52 22.7588,22.48 22.8325,22.3687L23.8288,20.64C23.8913,20.5325 23.8575,20.4 23.77,20.3225L22.7002,19.4925C22.72,19.3312 22.7393,19.17 22.7393,18.9988C22.7393,18.8275 22.7295,18.6712 22.7002,18.5113L23.76,17.6812C23.8475,17.5975 23.8812,17.4713 23.8188,17.3587L22.8225,15.63C22.7588,15.5225 22.6312,15.4787 22.51,15.5175L21.27,16.02C21.0107,15.82 20.7275,15.65 20.42,15.5275L20.2295,14.2087C20.22,14.0912 20.1123,13.9987 19.99,13.9987M18.9888,17.5C19.8188,17.5 20.4888,18.1687 20.4888,18.9988C20.4888,19.8288 19.8188,20.4975 18.9888,20.4975C18.1588,20.4975 17.49,19.8288 17.49,18.9988C17.49,18.1687 18.1588,17.5 18.9888,17.5Z"
android:strokeWidth="0.2"
android:strokeLineJoin="round" />
</vector>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M14.08,15.59L16.67,13H7V11H16.67L14.08,8.41L15.5,7L20.5,12L15.5,17L14.08,15.59M19,3A2,2 0 0,1 21,5V9.67L19,7.67V5H5V19H19V16.33L21,14.33V19A2,2 0 0,1 19,21H5C3.89,21 3,20.1 3,19V5C3,3.89 3.89,3 5,3H19Z" />
</vector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M3,7V5H5V4C5,2.89 5.9,2 7,2H13V9L15.5,7.5L18,9V2H19C20.05,2 21,2.95 21,4V20C21,21.05 20.05,22 19,22H7C5.95,22 5,21.05 5,20V19H3V17H5V13H3V11H5V7H3M7,11H5V13H7V11M7,7V5H5V7H7M7,19V17H5V19H7Z" />
</vector>

View File

@ -158,7 +158,6 @@
<string name="pref_title_edit_notification_settings">تعديل الاشعارات</string>
<string name="pref_title_notifications_enabled">الإخطارات</string>
<string name="pref_summary_notifications">للحساب %1$s</string>
<string name="pref_title_pull_notification_check_interval">مدة تحديث التحميل</string>
<string name="pref_title_notification_alerts">التنبيهات</string>
<string name="pref_title_notification_alert_sound">إعلام بالصوت</string>
<string name="pref_title_notification_alert_vibrate">إعلام بالاهتزار</string>

View File

@ -139,7 +139,6 @@
<string name="pref_title_notification_settings">Notificacions</string>
<string name="pref_title_edit_notification_settings">Edita les notificacions</string>
<string name="pref_title_notifications_enabled">Notificacions</string>
<string name="pref_title_pull_notification_check_interval">Comprova l\'interval</string>
<string name="pref_title_notification_alerts">Alertes</string>
<string name="pref_title_notification_alert_sound">Notifica amb un so</string>
<string name="pref_title_notification_alert_vibrate">Notifica amb una vibració</string>
@ -159,16 +158,6 @@
<string name="pref_title_show_replies">Mostra les respostes</string>
<string name="pref_title_show_media_preview">Mostra les previsualitzacions</string>
<string-array name="pull_notification_check_interval_names">
<item>15 minuts</item>
<item>20 minuts</item>
<item>25 minuts</item>
<item>30 minuts</item>
<item>45 minuts</item>
<item>1 hora</item>
<item>2 hores</item>
</string-array>
<string name="pref_default_post_privacy">Privacitat predeterminada dels toots</string>
<string name="pref_publishing">Publicació</string>

View File

@ -165,7 +165,6 @@
<string name="pref_title_edit_notification_settings">Golygu Hysbysiadau </string>
<string name="pref_title_notifications_enabled">Hysbysiadau</string>
<string name="pref_summary_notifications">i gyfrif %1$s</string>
<string name="pref_title_pull_notification_check_interval">Cadarnhau Amser</string>
<string name="pref_title_notification_alerts">Negeseuon pwysig</string>
<string name="pref_title_notification_alert_sound">Cael hysbysiad sŵn </string>
<string name="pref_title_notification_alert_vibrate">Cael hysbysiad crynu </string>
@ -200,16 +199,6 @@
<string name="pref_title_http_proxy_server">Gweinydd procsi HTTP </string>
<string name="pref_title_http_proxy_port">Porthol procsiHTTP </string>
<string-array name="pull_notification_check_interval_names">
<item>15 munud</item>
<item>20 munud</item>
<item>25 munud</item>
<item>30 munud</item>
<item>45 munud</item>
<item>1 awr</item>
<item>2 awr</item>
</string-array>
<string name="pref_default_post_privacy">Preifatrwydd postiadau rhagosodedig</string>
<string name="pref_publishing">Cyhoeddi </string>

View File

@ -134,16 +134,6 @@
<string name="pref_title_custom_tabs">Öffne Links in der App</string>
<string name="pref_title_hide_follow_button">Verstecke Button bei Bildlauf </string>
<string-array name="pull_notification_check_interval_names">
<item>15 Minuten</item>
<item>20 Minuten</item>
<item>25 Minuten</item>
<item>30 Minuten</item>
<item>45 Minuten</item>
<item>1 Stunde</item>
<item>2 Stunden</item>
</string-array>
<string-array name="post_privacy_names">
<item>Öffentlich</item>
<item>Nicht gelistet</item>
@ -214,7 +204,6 @@
<string name="pref_title_show_replies">Zeige Antworten</string>
<string name="pref_title_show_media_preview">Zeige Medienvorschauen</string>
<string name="pref_title_show_boosts">Zeige Boosts</string>
<string name="pref_title_pull_notification_check_interval">Überprüfungsintervall</string>
<string name="no_content">leer</string>
<string name="action_hide_media">Verstecke Medien</string>
<string name="pref_title_status_filter">Timeline-Filter</string>

View File

@ -160,7 +160,6 @@
<string name="pref_title_edit_notification_settings">Editar notificaciones</string>
<string name="pref_title_notifications_enabled">Notificaciones</string>
<string name="pref_summary_notifications">para la cuenta %1$s</string>
<string name="pref_title_pull_notification_check_interval">Intervalo</string>
<string name="pref_title_notification_alerts">Alertas</string>
<string name="pref_title_notification_alert_sound">Notificar con sonido</string>
<string name="pref_title_notification_alert_vibrate">Notificar con vibración</string>
@ -195,16 +194,6 @@
<string name="pref_title_http_proxy_server">Servidor del proxy HTTP</string>
<string name="pref_title_http_proxy_port">Puerto del proxy HTTP</string>
<string-array name="pull_notification_check_interval_names">
<item>15 minutos</item>
<item>20 minutos</item>
<item>25 minutos</item>
<item>30 minutos</item>
<item>45 minutos</item>
<item>1 hora</item>
<item>2 hora</item>
</string-array>
<string name="pref_default_post_privacy">Visibilidad por defecto</string>
<string name="pref_publishing">Publicaciones</string>

View File

@ -165,7 +165,6 @@
<string name="pref_title_edit_notification_settings" >ویرایش اعلان‌ها</string>
<string name="pref_title_notifications_enabled" >اعلان ها</string>
<string name="pref_summary_notifications" >برای حساب %1$s</string>
<string name="pref_title_pull_notification_check_interval" >فاصله را بررسی کن</string>
<string name="pref_title_notification_alerts" >هشدارها</string>
<string name="pref_title_notification_alert_sound" >اعلان با صدا</string>
<string name="pref_title_notification_alert_vibrate" >اعلان با لرزش</string>
@ -199,16 +198,6 @@
<string name="pref_title_http_proxy_enable" >فعالسازی پراکسی HTTP</string>
<string name="pref_title_http_proxy_server" >سرور پراکسی HTTP</string>
<string name="pref_title_http_proxy_port" >پورت پراکسی HTTP</string>
<string-array name="pull_notification_check_interval_names">
<item>۱۵ دقیقه</item>
<item>۲۰ دقیقه</item>
<item>۲۵ دقیقه</item>
<item>۳۰ دقیقه</item>
<item>۴۵ دقیقه</item>
<item>۱ ساعت</item>
<item>۲ ساعت</item>
</string-array>
<string name="pref_default_post_privacy">حریم خصوصی پیشفرض پست</string>
<string name="pref_publishing" >انتشار</string>

View File

@ -177,7 +177,6 @@
<string name="search_no_results">Aucun résultat</string>
<string name="dialog_unfollow_warning">Désirez-vous cesser de suivre ce compte\?</string>
<string name="pref_summary_notifications">pour le compte %1$s</string>
<string name="pref_title_pull_notification_check_interval">Interval de vérifications</string>
<string name="pref_title_app_theme">Theme de l\'application</string>
<string-array name="app_theme_names">
@ -193,16 +192,6 @@
<string name="pref_title_http_proxy_server">Adresse du serveur proxy HTTP</string>
<string name="pref_title_http_proxy_port">Port du serveur proxy HTTP</string>
<string-array name="pull_notification_check_interval_names">
<item>15 minutes</item>
<item>20 minutes</item>
<item>25 minutes</item>
<item>30 minutes</item>
<item>45 minutes</item>
<item>1 heure</item>
<item>2 heures</item>
</string-array>
<string name="pref_default_post_privacy">Confidentialité par defaut</string>
<string name="pref_publishing">Publier</string>

View File

@ -140,7 +140,6 @@
<string name="pref_title_notification_settings">Értesítések</string>
<string name="pref_title_edit_notification_settings">Értesítések szerkesztése</string>
<string name="pref_title_notifications_enabled">Értesítések</string>
<string name="pref_title_pull_notification_check_interval">Ellenőrzési időköz</string>
<string name="pref_title_notification_alerts">Figyelmeztetések</string>
<string name="pref_title_notification_alert_sound">Értesítés hanggal</string>
<string name="pref_title_notification_alert_vibrate">Értesítés rezgéssel</string>

View File

@ -165,7 +165,6 @@
<string name="pref_title_edit_notification_settings">Modifica Notifiche</string>
<string name="pref_title_notifications_enabled">Notifiche</string>
<string name="pref_summary_notifications">per l\'account %1$s</string>
<string name="pref_title_pull_notification_check_interval">Intervallo di controllo</string>
<string name="pref_title_notification_alerts">Allarmi</string>
<string name="pref_title_notification_alert_sound">Notifica con suoneria</string>
<string name="pref_title_notification_alert_vibrate">Notifica con vibrazione</string>
@ -200,16 +199,6 @@
<string name="pref_title_http_proxy_server">Server proxy HTTP</string>
<string name="pref_title_http_proxy_port">Porta proxy HTTP</string>
<string-array name="pull_notification_check_interval_names">
<item>15 minuti</item>
<item>20 minuti</item>
<item>25 minuti</item>
<item>30 minuti</item>
<item>45 minuti</item>
<item>1 ora</item>
<item>2 ore</item>
</string-array>
<string name="pref_default_post_privacy">Privacy di default dei post</string>
<string name="pref_publishing">Pubblicando</string>

View File

@ -159,7 +159,6 @@
<string name="pref_title_edit_notification_settings">通知を設定</string>
<string name="pref_title_notifications_enabled">プッシュ通知</string>
<string name="pref_summary_notifications">対象: %1$s</string>
<string name="pref_title_pull_notification_check_interval">通知の取得間隔</string>
<string name="pref_title_notification_alerts">通知時の動作</string>
<string name="pref_title_notification_alert_sound">着信音を鳴らす</string>
<string name="pref_title_notification_alert_vibrate">バイブレーションする</string>
@ -194,16 +193,6 @@
<string name="pref_title_http_proxy_server">HTTPプロキシーサーバー</string>
<string name="pref_title_http_proxy_port">HTTPプロキシーポート</string>
<string-array name="pull_notification_check_interval_names">
<item>15分</item>
<item>20分</item>
<item>25分</item>
<item>30分</item>
<item>45分</item>
<item>1時間</item>
<item>2時間</item>
</string-array>
<string name="pref_default_post_privacy">デフォルトの投稿プライバシー</string>
<string name="pref_publishing">投稿</string>

View File

@ -158,7 +158,6 @@
<string name="pref_title_edit_notification_settings">알림 설정 편집</string>
<string name="pref_title_notifications_enabled">알림</string>
<string name="pref_summary_notifications">%1$s 계정의 알림</string>
<string name="pref_title_pull_notification_check_interval">확인 간격</string>
<string name="pref_title_notification_alerts">알림</string>
<string name="pref_title_notification_alert_sound">소리로 알림</string>
<string name="pref_title_notification_alert_vibrate">진동으로 알림</string>

View File

@ -68,6 +68,8 @@
<item name="compound_button_color">@color/compound_button_color_dark</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>
<style name="AppTheme.ImageButton.Dark" parent="@style/Widget.AppCompat.Button.Borderless.Colored">

View File

@ -158,7 +158,6 @@
<string name="pref_title_edit_notification_settings">Meldingen bewerken</string>
<string name="pref_title_notifications_enabled">Meldingen</string>
<string name="pref_summary_notifications">voor account %1$s</string>
<string name="pref_title_pull_notification_check_interval">Controlefrequentie</string>
<string name="pref_title_notification_alerts">Waarschuwen met</string>
<string name="pref_title_notification_alert_sound">geluid</string>
<string name="pref_title_notification_alert_vibrate">trillen</string>
@ -193,16 +192,6 @@
<string name="pref_title_http_proxy_server">Serveradres van HTTP-proxy</string>
<string name="pref_title_http_proxy_port">Poort van HTTP-proxy</string>
<string-array name="pull_notification_check_interval_names">
<item>15 minuten</item>
<item>20 minuten</item>
<item>25 minuten</item>
<item>30 minuten</item>
<item>45 minuten</item>
<item>1 uur</item>
<item>2 uur</item>
</string-array>
<string name="pref_default_post_privacy">Standaardzichtbaarheid van jouw toots</string>
<string name="pref_publishing">Publiceren</string>

View File

@ -158,7 +158,6 @@
<string name="pref_title_edit_notification_settings">Modificar las notificacions</string>
<string name="pref_title_notifications_enabled">Notificacions</string>
<string name="pref_summary_notifications">pel compte %1$s</string>
<string name="pref_title_pull_notification_check_interval">Comprovar l\'interval</string>
<string name="pref_title_notification_alerts">Alèrtas</string>
<string name="pref_title_notification_alert_sound">Notificar amb un son</string>
<string name="pref_title_notification_alert_vibrate">Notificra amb una vibracion</string>
@ -194,16 +193,6 @@
<string name="pref_title_http_proxy_server">Servidor proxy HTTP</string>
<string name="pref_title_http_proxy_port">Pòrt del servidor proxy HTTP</string>
<string-array name="pull_notification_check_interval_names">
<item>15 minutas</item>
<item>20 minutas</item>
<item>25 minutas</item>
<item>30 minutas</item>
<item>45 minutas</item>
<item>1 ora</item>
<item>2 oras</item>
</string-array>
<string name="pref_default_post_privacy">Privacitat predeterminada dels tuts</string>
<string name="pref_publishing">Publicacion</string>

View File

@ -160,7 +160,6 @@
<string name="pref_title_edit_notification_settings">Edytuj powiadomienia</string>
<string name="pref_title_notifications_enabled">Powiadomienia</string>
<string name="pref_summary_notifications">dla konta %1$s</string>
<string name="pref_title_pull_notification_check_interval">Częstotliwość sprawdzania</string>
<string name="pref_title_notification_alerts">Alerty</string>
<string name="pref_title_notification_alert_sound">Powiadamiaj dźwiękiem</string>
<string name="pref_title_notification_alert_vibrate">Powiadamiaj wibracją</string>
@ -195,16 +194,6 @@
<string name="pref_title_http_proxy_server">Serwer proxy HTTP</string>
<string name="pref_title_http_proxy_port">Port proxy HTTP</string>
<string-array name="pull_notification_check_interval_names">
<item>15 minut</item>
<item>20 minut</item>
<item>25 minut</item>
<item>30 minut</item>
<item>45 minut</item>
<item>1 godzinę</item>
<item>2 godziny</item>
</string-array>
<string name="pref_default_post_privacy">Domyślne ustawienia prywatności wpisów</string>
<string name="pref_publishing">Publikowanie</string>

View File

@ -158,7 +158,6 @@
<string name="pref_title_edit_notification_settings">Editar Notificações</string>
<string name="pref_title_notifications_enabled">Notificações</string>
<string name="pref_summary_notifications">para a conta %1$s</string>
<string name="pref_title_pull_notification_check_interval">Intervalo de checagem</string>
<string name="pref_title_notification_alerts">Alertas</string>
<string name="pref_title_notification_alert_sound">Notificar com som</string>
<string name="pref_title_notification_alert_vibrate">Notificar com vibração</string>
@ -194,17 +193,6 @@
<string name="pref_title_http_proxy_server">Servidor do proxy HTTP</string>
<string name="pref_title_http_proxy_port">Porta do proxy HTTP</string>
<string-array name="pull_notification_check_interval_names">
<item>15 minutos</item>
<item>20 minutos</item>
<item>25 minutos</item>
<item>30 minutos</item>
<item>45 minutos</item>
<item>1 hora</item>
<item>2 horas</item>
</string-array>
<string name="pref_default_post_privacy">Privacidade padrão dos posts</string>
<string name="pref_publishing">Publicação</string>

View File

@ -156,7 +156,6 @@
<string name="pref_title_edit_notification_settings">Настройки уведомлений</string>
<string name="pref_title_notifications_enabled">Push-уведомления</string>
<string name="pref_summary_notifications">для аккаунта %1$s</string>
<string name="pref_title_pull_notification_check_interval">Интервал проверки</string>
<string name="pref_title_notification_alerts">Предупреждения</string>
<string name="pref_title_notification_alert_sound">Звуковые уведомления</string>
<string name="pref_title_notification_alert_vibrate">Использовать вибрацию</string>
@ -191,16 +190,6 @@
<string name="pref_title_http_proxy_server">Адрес сервера</string>
<string name="pref_title_http_proxy_port">Порт</string>
<string-array name="pull_notification_check_interval_names">
<item>15 минут</item>
<item>20 минут</item>
<item>25 минут</item>
<item>30 минут</item>
<item>45 минут</item>
<item>1 час</item>
<item>2 часа</item>
</string-array>
<string name="pref_default_post_privacy">Приватность статусов по умолчанию</string>
<string name="pref_publishing">Публикация</string>

View File

@ -164,7 +164,6 @@
<string name="pref_title_edit_notification_settings">Redigera notifieringar</string>
<string name="pref_title_notifications_enabled">Notifieringar</string>
<string name="pref_summary_notifications">för konto %1$s</string>
<string name="pref_title_pull_notification_check_interval">Uppdateringsintervall</string>
<string name="pref_title_notification_alerts">Alarm</string>
<string name="pref_title_notification_alert_sound">Meddela med ljud</string>
<string name="pref_title_notification_alert_vibrate">Meddela med vibration</string>
@ -198,16 +197,6 @@
<string name="pref_title_http_proxy_enable">Aktivera HTTP-proxy</string>
<string name="pref_title_http_proxy_server">HTTP proxy server</string>
<string name="pref_title_http_proxy_port">HTTP proxy port</string>
<string-array name="pull_notification_check_interval_names">
<item>15 minuter</item>
<item>20 minuter</item>
<item>25 minuter</item>
<item>30 minuter</item>
<item>45 minuter</item>
<item>1 timme</item>
<item>2 timmar</item>
</string-array>
<string name="pref_default_post_privacy">Standardinställning för inlägg</string>
<string name="pref_publishing">Publicering</string>

View File

@ -156,7 +156,6 @@
<string name="pref_title_edit_notification_settings">அறிவிப்புகளை திருத்த</string>
<string name="pref_title_notifications_enabled">அறிவிப்புகள்</string>
<string name="pref_summary_notifications">%1$s கணக்குக்கு</string>
<string name="pref_title_pull_notification_check_interval">சரிபார்க்கவேண்டிய இடைவேளை</string>
<string name="pref_title_notification_alerts">எச்சரிக்கைகள்</string>
<string name="pref_title_notification_alert_sound">ஒலி மூலம் தெரிவிக்கவும்</string>
<string name="pref_title_notification_alert_vibrate">அதிர்வுடன் தெரிவிக்கவும்</string>
@ -192,16 +191,6 @@
<string name="pref_title_http_proxy_server">HTTP ப்ராக்ஸி சேவையகம்</string>
<string name="pref_title_http_proxy_port">HTTP ப்ராக்ஸி போர்ட்</string>
<string-array name="pull_notification_check_interval_names">
<item>15 நிமிடங்கள்</item>
<item>20 நிமிடங்கள்</item>
<item>25 நிமிடங்கள்</item>
<item>30 நிமிடங்கள்</item>
<item>45 நிமிடங்கள்</item>
<item>1 மணி நேரம்</item>
<item>2 மணி நேரம்</item>
</string-array>
<string name="pref_default_post_privacy">தனியுரிமையில் பதிவிடுவதை முன்னிருப்பாக்கு</string>
<string name="pref_publishing">வெளியீடு</string>

View File

@ -3,13 +3,14 @@
<string name="error_generic">应用程序出现异常。</string>
<string name="error_empty">内容不能为空。</string>
<string name="error_invalid_domain">该域名无效</string>
<string name="error_failed_app_registration">无法连接到该实例</string>
<string name="error_failed_app_registration">无法连接此服务器</string>
<string name="error_no_web_browser_found">没有可用的浏览器。</string>
<string name="error_authorization_unknown">认证过程出现未知错误。</string>
<string name="error_authorization_denied">授权被拒绝。</string>
<string name="error_retrieving_oauth_token">无法获取登录信息。</string>
<string name="error_compose_character_limit">嘟文太长了!</string>
<string name="error_image_upload_size">文件大小限制 8MB。</string>
<string name="error_video_upload_size">视频文件大小限制 40MB。</string>
<string name="error_media_upload_type">无法上传此类型的文件。</string>
<string name="error_media_upload_opening">此文件无法打开。</string>
<string name="error_media_upload_permission">需要授予 Tusky 读取媒体文件的权限。</string>
@ -18,15 +19,17 @@
<string name="error_media_upload_sending">媒体文件上传失败。</string>
<string name="error_report_too_few_statuses">至少要报告 1 则嘟文。</string>
<string name="error_invalid_regex">正则表达式无效</string>
<string name="error_sender_account_gone">嘟文发送时出错。</string>
<string name="title_home">主页</string>
<string name="title_advanced">高级</string>
<string name="title_notifications">通知</string>
<string name="title_public_local">本站时间轴</string>
<string name="title_public_federated">跨站公共时间</string>
<string name="title_public_federated">跨站公共时间线</string>
<string name="title_view_thread">嘟文</string>
<string name="title_tag">#%s</string>
<string name="title_statuses">嘟文</string>
<string name="title_statuses_with_replies">嘟文和回复</string>
<string name="title_follows">正在关注</string>
<string name="title_followers">关注者</string>
<string name="title_favourites">我的收藏</string>
@ -35,6 +38,7 @@
<string name="title_follow_requests">关注请求</string>
<string name="title_edit_profile">编辑个人资料</string>
<string name="title_saved_toot">草稿</string>
<string name="title_licenses">开源协议</string>
<string name="status_username_format">\@%s</string>
<string name="status_boosted_format">%s 转嘟了</string>
@ -43,6 +47,8 @@
<string name="status_sensitive_media_directions">点击显示</string>
<string name="status_content_warning_show_more">显示更多</string>
<string name="status_content_warning_show_less">折叠内容</string>
<string name="status_content_show_more">展开</string>
<string name="status_content_show_less">折叠</string>
<string name="footer_empty">还没有内容,向下滑动即可刷新。</string>
@ -53,18 +59,21 @@
<string name="report_username_format">报告用户 @%s 的滥用行为</string>
<string name="report_comment_hint">报告更多信息</string>
<string name="action_quick_reply">快捷回复</string>
<string name="action_reply">回复</string>
<string name="action_reblog">转嘟</string>
<string name="action_favourite">收藏</string>
<string name="action_more">更多</string>
<string name="action_compose">发表新嘟文</string>
<string name="action_login">登录 Mastodon 帐号</string>
<string name="action_logout">登出</string>
<string name="action_logout_confirm">确定要出帐号 %1$s 吗?</string>
<string name="action_logout">退出登录</string>
<string name="action_logout_confirm">确定要退出帐号 %1$s 吗?</string>
<string name="action_follow">关注</string>
<string name="action_unfollow">取消关注</string>
<string name="action_block">屏蔽</string>
<string name="action_unblock">取消屏蔽</string>
<string name="action_hide_reblogs">隐藏转嘟</string>
<string name="action_show_reblogs">显示转嘟</string>
<string name="action_report">报告</string>
<string name="action_delete">删除</string>
<string name="action_send">发嘟</string>
@ -73,6 +82,7 @@
<string name="action_close">关闭</string>
<string name="action_view_profile">个人资料</string>
<string name="action_view_preferences">设置</string>
<string name="action_view_account_preferences">帐户设置</string>
<string name="action_view_favourites">我的收藏</string>
<string name="action_view_mutes">被隐藏的用户</string>
<string name="action_view_blocks">被屏蔽的用户</string>
@ -89,6 +99,7 @@
<string name="action_open_drawer">打开应用抽屉</string>
<string name="action_save">保存</string>
<string name="action_edit_profile">编辑个人资料</string>
<string name="action_edit_own_profile">编辑</string>
<string name="action_undo">撤销</string>
<string name="action_accept">接受</string>
<string name="action_reject">拒绝</string>
@ -104,10 +115,14 @@
<string name="send_status_link_to">分享链接到…</string>
<string name="send_status_content_to">分享嘟文到…</string>
<string name="send_media_to">分享媒体到…</string>
<string name="confirmation_reported">报告已发送!</string>
<string name="confirmation_unblocked">用户已被屏蔽</string>
<string name="confirmation_unmuted">用户已被隐藏</string>
<string name="confirmation_unblocked">已解除屏蔽</string>
<string name="confirmation_unmuted">已取消隐藏</string>
<string name="status_sent">已发送!</string>
<string name="status_sent_long">成功发布回复。</string>
<string name="hint_domain">域名</string>
<string name="hint_compose">有什么新鲜事?</string>
@ -118,6 +133,7 @@
<string name="search_no_results">没找到结果</string>
<string name="label_quick_reply">回复…</string>
<string name="label_avatar">头像</string>
<string name="label_header">标题</string>
@ -125,15 +141,16 @@
<string name="login_connection">正在连接…</string>
<string name="dialog_whats_an_instance">请输入你帐号所在的 Mastodon 站点的域名,比如 mastodon.socialpawoo.netmstdn.jpmao.daizhige.org<a href="https://instances.social">等等</a>
\n\n还没有 Mastodon 帐号?你也可以输入想注册的实例的域名,然后在该实例创建新的帐号并授权 Tusky 登入。
\n\n你帐号所在的 Mastodon 站点被称为 Mastodon 的一个「实例」instance。但是在 Mastodon 里,跨实例互动和站内互动一样简单。
\n\n可以前往 <a href="https://joinmastodon.org">https://joinmastodon.org</a> 了解更多信息。
<string name="dialog_whats_an_instance">请输入你帐号所在的 Mastodon 站点的域名,比如 pawoo.netmstdn.jpmao.daizhige.org<a href="https://instances.social">等等</a>
\n\n还没有 Mastodon 帐号?你也可以输入想注册的 Mastodon 站点的域名,然后在该服务器创建新的帐号并授权 Tusky 登入。
\n\n在 Mastodon 里,跨站互动和站内互动一样简单。可以前往 <a href="https://joinmastodon.org">https://joinmastodon.org</a> 了解更多信息。
</string>
<string name="dialog_title_finishing_media_upload">正在结束上传…</string>
<string name="dialog_message_uploading_media">正在上传…</string>
<string name="dialog_download_image">下载</string>
<string name="dialog_message_cancel_follow_request">移除关注请求?</string>
<string name="dialog_unfollow_warning">不再关注此用户?</string>
<string name="dialog_delete_toot_warning">删除这条嘟文?</string>
<string name="visibility_public">公开:所有人可见,并会出现在公共时间轴上</string>
<string name="visibility_unlisted">不公开:所有人可见,但不会出现在公共时间轴上</string>
@ -144,7 +161,6 @@
<string name="pref_title_edit_notification_settings">通知设置</string>
<string name="pref_title_notifications_enabled">通知</string>
<string name="pref_summary_notifications">帐号 %1$s</string>
<string name="pref_title_pull_notification_check_interval">检查间隔时间</string>
<string name="pref_title_notification_alerts">提醒</string>
<string name="pref_title_notification_alert_sound">通知铃声</string>
<string name="pref_title_notification_alert_vibrate">振动</string>
@ -156,6 +172,7 @@
<string name="pref_title_notification_filter_favourites">嘟文被收藏</string>
<string name="pref_title_appearance_settings">外观</string>
<string name="pref_title_app_theme">应用主题</string>
<string name="pref_title_timelines">时间线</string>
<string-array name="app_theme_names">
<item>黑夜</item>
@ -167,7 +184,7 @@
<string name="pref_title_browser_settings">浏览器</string>
<string name="pref_title_custom_tabs">使用 Chrome Custom Tabs</string>
<string name="pref_title_hide_follow_button">浏览时自动隐藏发嘟按钮</string>
<string name="pref_title_status_filter">时间过滤</string>
<string name="pref_title_status_filter">时间线过滤</string>
<string name="pref_title_status_tabs">选项卡</string>
<string name="pref_title_show_boosts">显示转嘟</string>
<string name="pref_title_show_replies">显示回复</string>
@ -179,18 +196,9 @@
<string name="pref_title_http_proxy_server">HTTP 代理服务器</string>
<string name="pref_title_http_proxy_port">HTTP 代理端口</string>
<string-array name="pull_notification_check_interval_names">
<item>15 分钟</item>
<item>20 分钟</item>
<item>25 分钟</item>
<item>30 分钟</item>
<item>45 分钟</item>
<item>1 小时</item>
<item>2 小时</item>
</string-array>
<string name="pref_default_post_privacy">嘟文默认可见范围</string>
<string name="pref_publishing">发布</string>
<string name="pref_failed_to_sync">同步失败设置</string>
<string-array name="post_privacy_names">
<item>公开</item>
@ -200,11 +208,21 @@
<string name="pref_status_text_size">字体大小</string>
<!--<string-array name="status_text_size_names">-->
<!--<item>小</item>-->
<!--<item>中</item>-->
<!--<item>大</item>-->
<!--</string-array>-->
<string-array name="status_text_size_names">
<item>最小</item>
<item></item>
<item>标准</item>
<item></item>
<item>最大</item>
</string-array>
<string name="pref_media_display">媒体显示</string>
<string-array name="media_display_names">
<item>自动隐藏敏感媒体</item>
<item>总是显示敏感媒体</item>
<item>总是隐藏所有媒体</item>
</string-array>
<string name="notification_channel_mention_name">被提及</string>
<string name="notification_channel_mention_descriptions">当有用户在嘟文中提及我时</string>
@ -270,7 +288,7 @@
<string name="action_lists">列表</string>
<string name="title_lists">列表</string>
<string name="title_list_timeline">列表公共时间</string>
<string name="title_list_timeline">列表公共时间线</string>
<string name="compose_active_account_description">使用帐号 %1$s 发布嘟文</string>
@ -280,10 +298,52 @@
<string name="lock_account_label">保护你的帐户(锁嘟)</string>
<string name="lock_account_label_description">你需要手动审核所有关注请求</string>
<string name="compose_save_draft">保存为草稿?</string>
<string name="send_toot_notification_title">正在发送...</string>
<string name="send_toot_notification_title">正在发送</string>
<string name="send_toot_notification_error_title">发送失败</string>
<string name="send_toot_notification_channel_name">嘟文发送中</string>
<string name="send_toot_notification_cancel_title">发送已被取消</string>
<string name="send_toot_notification_saved_content">嘟文已保存到草稿</string>
<string name="action_compose_shortcut">发表嘟文</string>
<string name="error_no_custom_emojis">当前服务器没有自定义的 Emoji 表情</string>
<string name="copy_to_clipboard_success">已复制到剪贴板</string>
<string name="emoji_style">Emoji 表情</string>
<string name="system_default">系统默认</string>
<string name="download_fonts">需要下载 Emoji 数据</string>
<string name="performing_lookup_title">正在查找…</string>
<string name="expand_collapse_all_statuses">展开/折叠所有嘟文</string>
<string name="action_open_toot">打开嘟文</string>
<string name="restart_required">需要重启应用</string>
<string name="restart_emoji">你需要重启 Tusky 才能生效</string>
<string name="later">以后</string>
<string name="restart">立即重启</string>
<string name="caption_systememoji">系统默认 Emoji 表情</string>
<string name="caption_blobmoji">Android 4.47.1 的黄馒头 Emoji 表情</string>
<string name="caption_twemoji">Mastodon 使用的 Emoji 表情</string>
<string name="download_failed">下载失败</string>
<string name="profile_badge_bot_text">Bot</string>
<string name="account_moved_description">%1$s 已迁移到:</string>
<string name="reblog_private">转嘟(可见者不变)</string>
<string name="unreblog_private">取消转嘟</string>
<string name="license_description">Tusky 使用了以下开源项目的源码:</string>
<string name="license_apache_2">以 Apache License 授权(复制如下)</string>
<string name="license_cc_by_4">CC-BY 4.0</string>
<string name="license_cc_by_sa_4">CC-BY-SA 4.0</string>
<string name="profile_metadata_label">个人资料附加信息</string>
<string name="profile_metadata_add">添加信息</string>
<string name="profile_metadata_label_label">标签</string>
<string name="profile_metadata_content_label">内容</string>
<string name="pref_title_absolute_time">嘟文显示精确时间</string>
<string name="label_remote_account">以下信息可能并不完整,要查看完整资料请使用浏览器打开。</string>
<string name="unpin_action">取消置顶</string>
<string name="pin_action">置顶</string>
</resources>

View File

@ -3,13 +3,14 @@
<string name="error_generic">應用程式出現異常。</string>
<string name="error_empty">內容不能為空。</string>
<string name="error_invalid_domain">該域名無效</string>
<string name="error_failed_app_registration">無法連接到該實例</string>
<string name="error_failed_app_registration">無法連接此伺服器</string>
<string name="error_no_web_browser_found">沒有可用的瀏覽器。</string>
<string name="error_authorization_unknown">認證過程出現未知錯誤。</string>
<string name="error_authorization_denied">授權被拒絕。</string>
<string name="error_retrieving_oauth_token">無法獲取登錄信息。</string>
<string name="error_compose_character_limit">嘟文太長了!</string>
<string name="error_image_upload_size">文件大小限制 8MB。</string>
<string name="error_video_upload_size">視頻文件大小限制 40MB。</string>
<string name="error_media_upload_type">無法上傳此類型的文件。</string>
<string name="error_media_upload_opening">此文件無法打開。</string>
<string name="error_media_upload_permission">需要授予 Tusky 讀取媒體文件的權限。</string>
@ -17,24 +18,27 @@
<string name="error_media_upload_image_or_video">無法在嘟文中同時插入視頻和圖片。</string>
<string name="error_media_upload_sending">媒體文件上傳失敗。</string>
<string name="error_report_too_few_statuses">至少要報告 1 則嘟文。</string>
<string name="error_invalid_regex">正則表達式無效</string>
<string name="error_invalid_regex">正規表示式無效</string>
<string name="error_sender_account_gone">嘟文發送時出錯。</string>
<string name="title_home">主頁</string>
<string name="title_advanced">高級</string>
<string name="title_notifications">通知</string>
<string name="title_public_local">本站時間軸</string>
<string name="title_public_federated">跨站公共時間</string>
<string name="title_public_federated">跨站公共時間</string>
<string name="title_view_thread">嘟文</string>
<string name="title_tag">#%s</string>
<string name="title_statuses">嘟文</string>
<string name="title_statuses_with_replies">嘟文和回覆</string>
<string name="title_follows">正在關注</string>
<string name="title_followers">關注者</string>
<string name="title_favourites">我的收藏</string>
<string name="title_mutes">被隱藏的用</string>
<string name="title_blocks">被屏蔽的用</string>
<string name="title_mutes">被隱藏的用</string>
<string name="title_blocks">被屏蔽的用</string>
<string name="title_follow_requests">關注請求</string>
<string name="title_edit_profile">編輯個人資料</string>
<string name="title_saved_toot">草稿</string>
<string name="title_licenses">開源授權</string>
<string name="status_username_format">\@%s</string>
<string name="status_boosted_format">%s 轉嘟了</string>
@ -43,6 +47,8 @@
<string name="status_sensitive_media_directions">點擊顯示</string>
<string name="status_content_warning_show_more">顯示更多</string>
<string name="status_content_warning_show_less">摺疊內容</string>
<string name="status_content_show_less">摺疊</string>
<string name="status_content_show_more">展開</string>
<string name="footer_empty">還沒有內容,向下滑動即可刷新。</string>
@ -50,21 +56,24 @@
<string name="notification_favourite_format">%s 收藏了你的嘟文</string>
<string name="notification_follow_format">%s 關注了你</string>
<string name="report_username_format">報告用 @%s 的濫用行為</string>
<string name="report_username_format">報告用 @%s 的濫用行為</string>
<string name="report_comment_hint">報告更多信息</string>
<string name="action_quick_reply">快捷回覆</string>
<string name="action_reply">回覆</string>
<string name="action_reblog">轉嘟</string>
<string name="action_favourite">收藏</string>
<string name="action_more">更多</string>
<string name="action_compose">發表新嘟文</string>
<string name="action_login">登錄 Mastodon 帳號</string>
<string name="action_logout">登出</string>
<string name="action_logout_confirm">確定要出帳號 %1$s 嗎?</string>
<string name="action_logout">退出登錄</string>
<string name="action_logout_confirm">確定要退出帳號 %1$s 嗎?</string>
<string name="action_follow">關注</string>
<string name="action_unfollow">取消關注</string>
<string name="action_block">屏蔽</string>
<string name="action_unblock">取消屏蔽</string>
<string name="action_hide_reblogs">隱藏轉嘟</string>
<string name="action_show_reblogs">顯示轉嘟</string>
<string name="action_report">報告</string>
<string name="action_delete">刪除</string>
<string name="action_send">發嘟</string>
@ -73,9 +82,10 @@
<string name="action_close">關閉</string>
<string name="action_view_profile">個人資料</string>
<string name="action_view_preferences">設置</string>
<string name="action_view_account_preferences">帳戶設置</string>
<string name="action_view_favourites">我的收藏</string>
<string name="action_view_mutes">被隱藏的用</string>
<string name="action_view_blocks">被屏蔽的用</string>
<string name="action_view_mutes">被隱藏的用</string>
<string name="action_view_blocks">被屏蔽的用</string>
<string name="action_view_follow_requests">關注請求</string>
<string name="action_view_media">媒體</string>
<string name="action_open_in_web">在瀏覽器中打開</string>
@ -89,10 +99,11 @@
<string name="action_open_drawer">打開應用抽屜</string>
<string name="action_save">保存</string>
<string name="action_edit_profile">編輯個人資料</string>
<string name="action_edit_own_profile">編輯</string>
<string name="action_undo">撤銷</string>
<string name="action_accept">接受</string>
<string name="action_reject">拒絕</string>
<string name="action_search"></string>
<string name="action_search"></string>
<string name="action_access_saved_toot">草稿</string>
<string name="action_toggle_visibility">設置嘟文可見範圍</string>
<string name="action_content_warning">設置內容提醒信息</string>
@ -104,10 +115,14 @@
<string name="send_status_link_to">分享鏈接到…</string>
<string name="send_status_content_to">分享嘟文到…</string>
<string name="send_media_to">分享媒體到…</string>
<string name="confirmation_reported">報告已發送!</string>
<string name="confirmation_unblocked">用户已被屏蔽</string>
<string name="confirmation_unmuted">用户已被隱藏</string>
<string name="confirmation_unblocked">已解除屏蔽</string>
<string name="confirmation_unmuted">已取消隱藏</string>
<string name="status_sent">已發送!</string>
<string name="status_sent_long">成功發佈回覆。</string>
<string name="hint_domain">域名</string>
<string name="hint_compose">有什麼新鮮事?</string>
@ -118,6 +133,7 @@
<string name="search_no_results">沒找到結果</string>
<string name="label_quick_reply">回覆…</string>
<string name="label_avatar">頭像</string>
<string name="label_header">標題</string>
@ -125,26 +141,26 @@
<string name="login_connection">正在連接…</string>
<string name="dialog_whats_an_instance">請輸入你帳號所在的 Mastodon 站點的域名,比如 mastodon.socialpawoo.netmstdn.jpmao.daizhige.org<a href="https://instances.social">等等</a>
\n\n還沒有 Mastodon 帳號?你也可以輸入想註冊的實例的域名,然後在該實例創建新的帳號並授權 Tusky 登入。
\n\n你帳號所在的 Mastodon 站點被稱為 Mastodon 的一個「實例」instance。但是在 Mastodon 裏,跨實例互動和站內互動一樣簡單。
\n\n可以前往 <a href="https://joinmastodon.org">https://joinmastodon.org</a> 瞭解更多信息。
<string name="dialog_whats_an_instance">請輸入你帳號所在的 Mastodon 站點的域名,比如 pawoo.netmstdn.jpmao.daizhige.org<a href="https://instances.social">等等</a>
\n\n還沒有 Mastodon 帳號?你也可以輸入想註冊的 Mastodon 站點的域名,然後在該伺服器創建新的帳號並授權 Tusky 登入。
\n\n在 Mastodon 裡,跨站互動和站內互動一樣簡單。可以前往 <a href="https://joinmastodon.org">https://joinmastodon.org</a> 瞭解更多信息。
</string>
<string name="dialog_title_finishing_media_upload">正在結束上傳…</string>
<string name="dialog_message_uploading_media">正在上傳…</string>
<string name="dialog_download_image">下載</string>
<string name="dialog_unfollow_warning">不再關注此用户?</string>
<string name="dialog_message_cancel_follow_request">移除關注請求?</string>
<string name="dialog_unfollow_warning">不再關注此用戶?</string>
<string name="dialog_delete_toot_warning">刪除這條嘟文?</string>
<string name="visibility_public">公開:所有人可見,並會出現在公共時間軸上</string>
<string name="visibility_unlisted">不公開:所有人可見,但不會出現在公共時間軸上</string>
<string name="visibility_private">僅關注者:只有經過你確認後關注你的用可見</string>
<string name="visibility_direct">私信:只有被提及的用可見</string>
<string name="visibility_private">僅關注者:只有經過你確認後關注你的用可見</string>
<string name="visibility_direct">私信:只有被提及的用可見</string>
<string name="pref_title_notification_settings">通知</string>
<string name="pref_title_edit_notification_settings">通知設置</string>
<string name="pref_title_notifications_enabled">通知</string>
<string name="pref_summary_notifications">帳號 %1$s</string>
<string name="pref_title_pull_notification_check_interval">檢查間隔時間</string>
<string name="pref_title_notification_alerts">提醒</string>
<string name="pref_title_notification_alert_sound">通知鈴聲</string>
<string name="pref_title_notification_alert_vibrate">振動</string>
@ -156,6 +172,7 @@
<string name="pref_title_notification_filter_favourites">嘟文被收藏</string>
<string name="pref_title_appearance_settings">外觀</string>
<string name="pref_title_app_theme">應用主題</string>
<string name="pref_title_timelines">時間線</string>
<string-array name="app_theme_names">
<item>黑夜</item>
@ -167,30 +184,21 @@
<string name="pref_title_browser_settings">瀏覽器</string>
<string name="pref_title_custom_tabs">使用 Chrome Custom Tabs</string>
<string name="pref_title_hide_follow_button">瀏覽時自動隱藏發嘟按鈕</string>
<string name="pref_title_status_filter">時間過濾</string>
<string name="pref_title_status_filter">時間過濾</string>
<string name="pref_title_status_tabs">選項卡</string>
<string name="pref_title_show_boosts">顯示轉嘟</string>
<string name="pref_title_show_replies">顯示回覆</string>
<string name="pref_title_filter_regex">使用正則表達式過濾</string>
<string name="pref_title_filter_regex">使用正規表示式過濾</string>
<string name="pref_title_show_media_preview">顯示預覽圖</string>
<string name="pref_title_proxy_settings">代理</string>
<string name="pref_title_http_proxy_settings">HTTP 代理</string>
<string name="pref_title_http_proxy_enable">啟用 HTTP 代理</string>
<string name="pref_title_http_proxy_server">HTTP 代理服</string>
<string name="pref_title_http_proxy_server">HTTP 代理服器</string>
<string name="pref_title_http_proxy_port">HTTP 代理端口</string>
<string-array name="pull_notification_check_interval_names">
<item>15 分鐘</item>
<item>20 分鐘</item>
<item>25 分鐘</item>
<item>30 分鐘</item>
<item>45 分鐘</item>
<item>1 小時</item>
<item>2 小時</item>
</string-array>
<string name="pref_default_post_privacy">嘟文默認可見範圍</string>
<string name="pref_publishing">發佈</string>
<string name="pref_failed_to_sync">同步失敗設置</string>
<string-array name="post_privacy_names">
<item>公開</item>
@ -200,20 +208,30 @@
<string name="pref_status_text_size">字體大小</string>
<!--<string-array name="status_text_size_names">-->
<!--<item>小</item>-->
<!--<item>中</item>-->
<!--<item>大</item>-->
<!--</string-array>-->
<string-array name="status_text_size_names">
<item>最小</item>
<item></item>
<item>標準</item>
<item></item>
<item>最大</item>
</string-array>
<string name="pref_media_display">媒體顯示</string>
<string-array name="media_display_names">
<item>自動隱藏敏感媒體</item>
<item>總是顯示敏感媒體</item>
<item>總是隱藏所有媒體</item>
</string-array>
<string name="notification_channel_mention_name">被提及</string>
<string name="notification_channel_mention_descriptions">當有用户在嘟文中提及我時</string>
<string name="notification_channel_mention_descriptions">當有用在嘟文中提及我時</string>
<string name="notification_channel_follow_name">關注者</string>
<string name="notification_channel_follow_description">當有用户關注我時</string>
<string name="notification_channel_follow_description">當有用關注我時</string>
<string name="notification_channel_boost_name">轉嘟</string>
<string name="notification_channel_boost_description">當有用户轉嘟了我的嘟文時</string>
<string name="notification_channel_boost_description">當有用轉嘟了我的嘟文時</string>
<string name="notification_channel_favourite_name">被收藏</string>
<string name="notification_channel_favourite_description">當有用户收藏了我的嘟文時</string>
<string name="notification_channel_favourite_description">當有用收藏了我的嘟文時</string>
<string name="notification_mention_format">%s 提及了你</string>
@ -270,20 +288,62 @@
<string name="action_lists">列表</string>
<string name="title_lists">列表</string>
<string name="title_list_timeline">列表公共時間</string>
<string name="title_list_timeline">列表公共時間</string>
<string name="compose_active_account_description">使用帳號 %1$s 發佈嘟文</string>
<string name="error_failed_set_caption">設置圖片標題失敗。</string>
<string name="action_set_caption">設置圖片標題</string>
<string name="action_remove_media">移除</string>
<string name="lock_account_label">保護你的帳(鎖嘟)</string>
<string name="lock_account_label">保護你的帳(鎖嘟)</string>
<string name="lock_account_label_description">你需要手動審核所有關注請求</string>
<string name="compose_save_draft">保存為草稿?</string>
<string name="send_toot_notification_title">正在發送...</string>
<string name="send_toot_notification_title">正在發送</string>
<string name="send_toot_notification_error_title">發送失敗</string>
<string name="send_toot_notification_channel_name">嘟文發送中</string>
<string name="send_toot_notification_cancel_title">發送已被取消</string>
<string name="send_toot_notification_saved_content">嘟文已保存到草稿</string>
<string name="action_compose_shortcut">發表嘟文</string>
<string name="error_no_custom_emojis">當前服務器沒有自定義的 Emoji 表情</string>
<string name="copy_to_clipboard_success">已複製到剪貼板</string>
<string name="emoji_style">Emoji 表情</string>
<string name="system_default">系統默認</string>
<string name="download_fonts">需要下載 Emoji 數據</string>
<string name="performing_lookup_title">正在查找…</string>
<string name="expand_collapse_all_statuses">展開/摺疊所有嘟文</string>
<string name="action_open_toot">打開嘟文</string>
<string name="restart_required">需要重啟應用</string>
<string name="restart_emoji">你需要重啟 Tusky 才能生效</string>
<string name="later">以後</string>
<string name="restart">立即重啟</string>
<string name="caption_systememoji">系統默認 Emoji 表情</string>
<string name="caption_blobmoji">Android 4.47.1 的黃饅頭 Emoji 表情</string>
<string name="caption_twemoji">Mastodon 使用的 Emoji 表情</string>
<string name="download_failed">下載失敗</string>
<string name="profile_badge_bot_text">Bot</string>
<string name="account_moved_description">%1$s 已遷移到:</string>
<string name="reblog_private">轉嘟(可見者不變)</string>
<string name="unreblog_private">取消轉嘟</string>
<string name="license_description">Tusky 使用了以下開源項目的源碼:</string>
<string name="license_apache_2">以 Apache License 授權(複製如下)</string>
<string name="license_cc_by_4">CC-BY 4.0</string>
<string name="license_cc_by_sa_4">CC-BY-SA 4.0</string>
<string name="profile_metadata_label">个人资料附加信息</string>
<string name="profile_metadata_add">添加信息</string>
<string name="profile_metadata_label_label">標籤</string>
<string name="profile_metadata_content_label">內容</string>
<string name="pref_title_absolute_time">嘟文顯示精確時間</string>
<string name="label_remote_account">以下信息可能並不完整,要查看完整資料請使用瀏覽器打開。</string>
<string name="unpin_action">取消置頂</string>
<string name="pin_action">置頂</string>
</resources>

View File

@ -3,13 +3,14 @@
<string name="error_generic">應用程式出現異常。</string>
<string name="error_empty">內容不能為空。</string>
<string name="error_invalid_domain">該域名無效</string>
<string name="error_failed_app_registration">無法連接到該實例</string>
<string name="error_failed_app_registration">無法連接此伺服器</string>
<string name="error_no_web_browser_found">沒有可用的瀏覽器。</string>
<string name="error_authorization_unknown">認證過程出現未知錯誤。</string>
<string name="error_authorization_denied">授權被拒絕。</string>
<string name="error_retrieving_oauth_token">無法獲取登錄信息。</string>
<string name="error_compose_character_limit">嘟文太長了!</string>
<string name="error_image_upload_size">文件大小限制 8MB。</string>
<string name="error_video_upload_size">視頻文件大小限制 40MB。</string>
<string name="error_media_upload_type">無法上傳此類型的文件。</string>
<string name="error_media_upload_opening">此文件無法打開。</string>
<string name="error_media_upload_permission">需要授予 Tusky 讀取媒體文件的權限。</string>
@ -17,24 +18,27 @@
<string name="error_media_upload_image_or_video">無法在嘟文中同時插入視頻和圖片。</string>
<string name="error_media_upload_sending">媒體文件上傳失敗。</string>
<string name="error_report_too_few_statuses">至少要報告 1 則嘟文。</string>
<string name="error_invalid_regex">正則表達式無效</string>
<string name="error_invalid_regex">正規表示式無效</string>
<string name="error_sender_account_gone">嘟文發送時出錯。</string>
<string name="title_home">主頁</string>
<string name="title_advanced">高級</string>
<string name="title_notifications">通知</string>
<string name="title_public_local">本站時間軸</string>
<string name="title_public_federated">跨站公共時間</string>
<string name="title_public_federated">跨站公共時間</string>
<string name="title_view_thread">嘟文</string>
<string name="title_tag">#%s</string>
<string name="title_statuses">嘟文</string>
<string name="title_statuses_with_replies">嘟文和回覆</string>
<string name="title_follows">正在關注</string>
<string name="title_followers">關注者</string>
<string name="title_favourites">我的收藏</string>
<string name="title_mutes">被隱藏的用</string>
<string name="title_blocks">被屏蔽的用</string>
<string name="title_mutes">被隱藏的用</string>
<string name="title_blocks">被屏蔽的用</string>
<string name="title_follow_requests">關注請求</string>
<string name="title_edit_profile">編輯個人資料</string>
<string name="title_saved_toot">草稿</string>
<string name="title_licenses">開源授權</string>
<string name="status_username_format">\@%s</string>
<string name="status_boosted_format">%s 轉嘟了</string>
@ -43,6 +47,8 @@
<string name="status_sensitive_media_directions">點擊顯示</string>
<string name="status_content_warning_show_more">顯示更多</string>
<string name="status_content_warning_show_less">摺疊內容</string>
<string name="status_content_show_less">摺疊</string>
<string name="status_content_show_more">展開</string>
<string name="footer_empty">還沒有內容,向下滑動即可刷新。</string>
@ -50,21 +56,24 @@
<string name="notification_favourite_format">%s 收藏了你的嘟文</string>
<string name="notification_follow_format">%s 關注了你</string>
<string name="report_username_format">報告用 @%s 的濫用行為</string>
<string name="report_username_format">報告用 @%s 的濫用行為</string>
<string name="report_comment_hint">報告更多信息</string>
<string name="action_quick_reply">快捷回覆</string>
<string name="action_reply">回覆</string>
<string name="action_reblog">轉嘟</string>
<string name="action_favourite">收藏</string>
<string name="action_more">更多</string>
<string name="action_compose">發表新嘟文</string>
<string name="action_login">登錄 Mastodon 帳號</string>
<string name="action_logout">登出</string>
<string name="action_logout_confirm">確定要出帳號 %1$s 嗎?</string>
<string name="action_logout">退出登錄</string>
<string name="action_logout_confirm">確定要退出帳號 %1$s 嗎?</string>
<string name="action_follow">關注</string>
<string name="action_unfollow">取消關注</string>
<string name="action_block">屏蔽</string>
<string name="action_unblock">取消屏蔽</string>
<string name="action_hide_reblogs">隱藏轉嘟</string>
<string name="action_show_reblogs">顯示轉嘟</string>
<string name="action_report">報告</string>
<string name="action_delete">刪除</string>
<string name="action_send">發嘟</string>
@ -73,9 +82,10 @@
<string name="action_close">關閉</string>
<string name="action_view_profile">個人資料</string>
<string name="action_view_preferences">設置</string>
<string name="action_view_account_preferences">帳戶設置</string>
<string name="action_view_favourites">我的收藏</string>
<string name="action_view_mutes">被隱藏的用</string>
<string name="action_view_blocks">被屏蔽的用</string>
<string name="action_view_mutes">被隱藏的用</string>
<string name="action_view_blocks">被屏蔽的用</string>
<string name="action_view_follow_requests">關注請求</string>
<string name="action_view_media">媒體</string>
<string name="action_open_in_web">在瀏覽器中打開</string>
@ -89,10 +99,11 @@
<string name="action_open_drawer">打開應用抽屜</string>
<string name="action_save">保存</string>
<string name="action_edit_profile">編輯個人資料</string>
<string name="action_edit_own_profile">編輯</string>
<string name="action_undo">撤銷</string>
<string name="action_accept">接受</string>
<string name="action_reject">拒絕</string>
<string name="action_search"></string>
<string name="action_search"></string>
<string name="action_access_saved_toot">草稿</string>
<string name="action_toggle_visibility">設置嘟文可見範圍</string>
<string name="action_content_warning">設置內容提醒信息</string>
@ -104,10 +115,14 @@
<string name="send_status_link_to">分享鏈接到…</string>
<string name="send_status_content_to">分享嘟文到…</string>
<string name="send_media_to">分享媒體到…</string>
<string name="confirmation_reported">報告已發送!</string>
<string name="confirmation_unblocked">用户已被屏蔽</string>
<string name="confirmation_unmuted">用户已被隱藏</string>
<string name="confirmation_unblocked">已解除屏蔽</string>
<string name="confirmation_unmuted">已取消隱藏</string>
<string name="status_sent">已發送!</string>
<string name="status_sent_long">成功發佈回覆。</string>
<string name="hint_domain">域名</string>
<string name="hint_compose">有什麼新鮮事?</string>
@ -118,6 +133,7 @@
<string name="search_no_results">沒找到結果</string>
<string name="label_quick_reply">回覆…</string>
<string name="label_avatar">頭像</string>
<string name="label_header">標題</string>
@ -125,26 +141,26 @@
<string name="login_connection">正在連接…</string>
<string name="dialog_whats_an_instance">請輸入你帳號所在的 Mastodon 站點的域名,比如 mastodon.socialpawoo.netmstdn.jpmao.daizhige.org<a href="https://instances.social">等等</a>
\n\n還沒有 Mastodon 帳號?你也可以輸入想註冊的實例的域名,然後在該實例創建新的帳號並授權 Tusky 登入。
\n\n你帳號所在的 Mastodon 站點被稱為 Mastodon 的一個「實例」instance。但是在 Mastodon 裏,跨實例互動和站內互動一樣簡單。
\n\n可以前往 <a href="https://joinmastodon.org">https://joinmastodon.org</a> 瞭解更多信息。
<string name="dialog_whats_an_instance">請輸入你帳號所在的 Mastodon 站點的域名,比如 pawoo.netmstdn.jpmao.daizhige.org<a href="https://instances.social">等等</a>
\n\n還沒有 Mastodon 帳號?你也可以輸入想註冊的 Mastodon 站點的域名,然後在該伺服器創建新的帳號並授權 Tusky 登入。
\n\n在 Mastodon 裡,跨站互動和站內互動一樣簡單。可以前往 <a href="https://joinmastodon.org">https://joinmastodon.org</a> 瞭解更多信息。
</string>
<string name="dialog_title_finishing_media_upload">正在結束上傳…</string>
<string name="dialog_message_uploading_media">正在上傳…</string>
<string name="dialog_download_image">下載</string>
<string name="dialog_unfollow_warning">不再關注此用户?</string>
<string name="dialog_message_cancel_follow_request">移除關注請求?</string>
<string name="dialog_unfollow_warning">不再關注此用戶?</string>
<string name="dialog_delete_toot_warning">刪除這條嘟文?</string>
<string name="visibility_public">公開:所有人可見,並會出現在公共時間軸上</string>
<string name="visibility_unlisted">不公開:所有人可見,但不會出現在公共時間軸上</string>
<string name="visibility_private">僅關注者:只有經過你確認後關注你的用可見</string>
<string name="visibility_direct">私信:只有被提及的用可見</string>
<string name="visibility_private">僅關注者:只有經過你確認後關注你的用可見</string>
<string name="visibility_direct">私信:只有被提及的用可見</string>
<string name="pref_title_notification_settings">通知</string>
<string name="pref_title_edit_notification_settings">通知設置</string>
<string name="pref_title_notifications_enabled">通知</string>
<string name="pref_summary_notifications">帳號 %1$s</string>
<string name="pref_title_pull_notification_check_interval">檢查間隔時間</string>
<string name="pref_title_notification_alerts">提醒</string>
<string name="pref_title_notification_alert_sound">通知鈴聲</string>
<string name="pref_title_notification_alert_vibrate">振動</string>
@ -156,6 +172,7 @@
<string name="pref_title_notification_filter_favourites">嘟文被收藏</string>
<string name="pref_title_appearance_settings">外觀</string>
<string name="pref_title_app_theme">應用主題</string>
<string name="pref_title_timelines">時間線</string>
<string-array name="app_theme_names">
<item>黑夜</item>
@ -167,30 +184,21 @@
<string name="pref_title_browser_settings">瀏覽器</string>
<string name="pref_title_custom_tabs">使用 Chrome Custom Tabs</string>
<string name="pref_title_hide_follow_button">瀏覽時自動隱藏發嘟按鈕</string>
<string name="pref_title_status_filter">時間過濾</string>
<string name="pref_title_status_filter">時間過濾</string>
<string name="pref_title_status_tabs">選項卡</string>
<string name="pref_title_show_boosts">顯示轉嘟</string>
<string name="pref_title_show_replies">顯示回覆</string>
<string name="pref_title_filter_regex">使用正則表達式過濾</string>
<string name="pref_title_filter_regex">使用正規表示式過濾</string>
<string name="pref_title_show_media_preview">顯示預覽圖</string>
<string name="pref_title_proxy_settings">代理</string>
<string name="pref_title_http_proxy_settings">HTTP 代理</string>
<string name="pref_title_http_proxy_enable">啟用 HTTP 代理</string>
<string name="pref_title_http_proxy_server">HTTP 代理服</string>
<string name="pref_title_http_proxy_server">HTTP 代理服器</string>
<string name="pref_title_http_proxy_port">HTTP 代理端口</string>
<string-array name="pull_notification_check_interval_names">
<item>15 分鐘</item>
<item>20 分鐘</item>
<item>25 分鐘</item>
<item>30 分鐘</item>
<item>45 分鐘</item>
<item>1 小時</item>
<item>2 小時</item>
</string-array>
<string name="pref_default_post_privacy">嘟文默認可見範圍</string>
<string name="pref_publishing">發佈</string>
<string name="pref_failed_to_sync">同步失敗設置</string>
<string-array name="post_privacy_names">
<item>公開</item>
@ -200,20 +208,30 @@
<string name="pref_status_text_size">字體大小</string>
<!--<string-array name="status_text_size_names">-->
<!--<item>小</item>-->
<!--<item>中</item>-->
<!--<item>大</item>-->
<!--</string-array>-->
<string-array name="status_text_size_names">
<item>最小</item>
<item></item>
<item>標準</item>
<item></item>
<item>最大</item>
</string-array>
<string name="pref_media_display">媒體顯示</string>
<string-array name="media_display_names">
<item>自動隱藏敏感媒體</item>
<item>總是顯示敏感媒體</item>
<item>總是隱藏所有媒體</item>
</string-array>
<string name="notification_channel_mention_name">被提及</string>
<string name="notification_channel_mention_descriptions">當有用户在嘟文中提及我時</string>
<string name="notification_channel_mention_descriptions">當有用在嘟文中提及我時</string>
<string name="notification_channel_follow_name">關注者</string>
<string name="notification_channel_follow_description">當有用户關注我時</string>
<string name="notification_channel_follow_description">當有用關注我時</string>
<string name="notification_channel_boost_name">轉嘟</string>
<string name="notification_channel_boost_description">當有用户轉嘟了我的嘟文時</string>
<string name="notification_channel_boost_description">當有用轉嘟了我的嘟文時</string>
<string name="notification_channel_favourite_name">被收藏</string>
<string name="notification_channel_favourite_description">當有用户收藏了我的嘟文時</string>
<string name="notification_channel_favourite_description">當有用收藏了我的嘟文時</string>
<string name="notification_mention_format">%s 提及了你</string>
@ -270,20 +288,62 @@
<string name="action_lists">列表</string>
<string name="title_lists">列表</string>
<string name="title_list_timeline">列表公共時間</string>
<string name="title_list_timeline">列表公共時間</string>
<string name="compose_active_account_description">使用帳號 %1$s 發佈嘟文</string>
<string name="error_failed_set_caption">設置圖片標題失敗。</string>
<string name="action_set_caption">設置圖片標題</string>
<string name="action_remove_media">移除</string>
<string name="lock_account_label">保護你的帳(鎖嘟)</string>
<string name="lock_account_label">保護你的帳(鎖嘟)</string>
<string name="lock_account_label_description">你需要手動審核所有關注請求</string>
<string name="compose_save_draft">保存為草稿?</string>
<string name="send_toot_notification_title">正在發送...</string>
<string name="send_toot_notification_title">正在發送</string>
<string name="send_toot_notification_error_title">發送失敗</string>
<string name="send_toot_notification_channel_name">嘟文發送中</string>
<string name="send_toot_notification_cancel_title">發送已被取消</string>
<string name="send_toot_notification_saved_content">嘟文已保存到草稿</string>
<string name="action_compose_shortcut">發表嘟文</string>
<string name="error_no_custom_emojis">當前服務器沒有自定義的 Emoji 表情</string>
<string name="copy_to_clipboard_success">已複製到剪貼板</string>
<string name="emoji_style">Emoji 表情</string>
<string name="system_default">系統默認</string>
<string name="download_fonts">需要下載 Emoji 數據</string>
<string name="performing_lookup_title">正在查找…</string>
<string name="expand_collapse_all_statuses">展開/摺疊所有嘟文</string>
<string name="action_open_toot">打開嘟文</string>
<string name="restart_required">需要重啟應用</string>
<string name="restart_emoji">你需要重啟 Tusky 才能生效</string>
<string name="later">以後</string>
<string name="restart">立即重啟</string>
<string name="caption_systememoji">系統默認 Emoji 表情</string>
<string name="caption_blobmoji">Android 4.47.1 的黃饅頭 Emoji 表情</string>
<string name="caption_twemoji">Mastodon 使用的 Emoji 表情</string>
<string name="download_failed">下載失敗</string>
<string name="profile_badge_bot_text">Bot</string>
<string name="account_moved_description">%1$s 已遷移到:</string>
<string name="reblog_private">轉嘟(可見者不變)</string>
<string name="unreblog_private">取消轉嘟</string>
<string name="license_description">Tusky 使用了以下開源項目的源碼:</string>
<string name="license_apache_2">以 Apache License 授權(複製如下)</string>
<string name="license_cc_by_4">CC-BY 4.0</string>
<string name="license_cc_by_sa_4">CC-BY-SA 4.0</string>
<string name="profile_metadata_label">个人资料附加信息</string>
<string name="profile_metadata_add">添加信息</string>
<string name="profile_metadata_label_label">標籤</string>
<string name="profile_metadata_content_label">內容</string>
<string name="pref_title_absolute_time">嘟文顯示精確時間</string>
<string name="label_remote_account">以下信息可能並不完整,要查看完整資料請使用瀏覽器打開。</string>
<string name="unpin_action">取消置頂</string>
<string name="pin_action">置頂</string>
</resources>

View File

@ -3,13 +3,14 @@
<string name="error_generic">应用程序出现异常。</string>
<string name="error_empty">内容不能为空。</string>
<string name="error_invalid_domain">该域名无效</string>
<string name="error_failed_app_registration">无法连接到该实例</string>
<string name="error_failed_app_registration">无法连接此服务器</string>
<string name="error_no_web_browser_found">没有可用的浏览器。</string>
<string name="error_authorization_unknown">认证过程出现未知错误。</string>
<string name="error_authorization_denied">授权被拒绝。</string>
<string name="error_retrieving_oauth_token">无法获取登录信息。</string>
<string name="error_compose_character_limit">嘟文太长了!</string>
<string name="error_image_upload_size">文件大小限制 8MB。</string>
<string name="error_video_upload_size">视频文件大小限制 40MB。</string>
<string name="error_media_upload_type">无法上传此类型的文件。</string>
<string name="error_media_upload_opening">此文件无法打开。</string>
<string name="error_media_upload_permission">需要授予 Tusky 读取媒体文件的权限。</string>
@ -18,15 +19,17 @@
<string name="error_media_upload_sending">媒体文件上传失败。</string>
<string name="error_report_too_few_statuses">至少要报告 1 则嘟文。</string>
<string name="error_invalid_regex">正则表达式无效</string>
<string name="error_sender_account_gone">嘟文发送时出错。</string>
<string name="title_home">主页</string>
<string name="title_advanced">高级</string>
<string name="title_notifications">通知</string>
<string name="title_public_local">本站时间轴</string>
<string name="title_public_federated">跨站公共时间</string>
<string name="title_public_federated">跨站公共时间线</string>
<string name="title_view_thread">嘟文</string>
<string name="title_tag">#%s</string>
<string name="title_statuses">嘟文</string>
<string name="title_statuses_with_replies">嘟文和回复</string>
<string name="title_follows">正在关注</string>
<string name="title_followers">关注者</string>
<string name="title_favourites">我的收藏</string>
@ -35,6 +38,7 @@
<string name="title_follow_requests">关注请求</string>
<string name="title_edit_profile">编辑个人资料</string>
<string name="title_saved_toot">草稿</string>
<string name="title_licenses">开源协议</string>
<string name="status_username_format">\@%s</string>
<string name="status_boosted_format">%s 转嘟了</string>
@ -43,6 +47,8 @@
<string name="status_sensitive_media_directions">点击显示</string>
<string name="status_content_warning_show_more">显示更多</string>
<string name="status_content_warning_show_less">折叠内容</string>
<string name="status_content_show_more">展开</string>
<string name="status_content_show_less">折叠</string>
<string name="footer_empty">还没有内容,向下滑动即可刷新。</string>
@ -53,18 +59,21 @@
<string name="report_username_format">报告用户 @%s 的滥用行为</string>
<string name="report_comment_hint">报告更多信息</string>
<string name="action_quick_reply">快捷回复</string>
<string name="action_reply">回复</string>
<string name="action_reblog">转嘟</string>
<string name="action_favourite">收藏</string>
<string name="action_more">更多</string>
<string name="action_compose">发表新嘟文</string>
<string name="action_login">登录 Mastodon 帐号</string>
<string name="action_logout">登出</string>
<string name="action_logout_confirm">确定要出帐号 %1$s 吗?</string>
<string name="action_logout">退出登录</string>
<string name="action_logout_confirm">确定要退出帐号 %1$s 吗?</string>
<string name="action_follow">关注</string>
<string name="action_unfollow">取消关注</string>
<string name="action_block">屏蔽</string>
<string name="action_unblock">取消屏蔽</string>
<string name="action_hide_reblogs">隐藏转嘟</string>
<string name="action_show_reblogs">显示转嘟</string>
<string name="action_report">报告</string>
<string name="action_delete">删除</string>
<string name="action_send">发嘟</string>
@ -73,6 +82,7 @@
<string name="action_close">关闭</string>
<string name="action_view_profile">个人资料</string>
<string name="action_view_preferences">设置</string>
<string name="action_view_account_preferences">帐户设置</string>
<string name="action_view_favourites">我的收藏</string>
<string name="action_view_mutes">被隐藏的用户</string>
<string name="action_view_blocks">被屏蔽的用户</string>
@ -89,6 +99,7 @@
<string name="action_open_drawer">打开应用抽屉</string>
<string name="action_save">保存</string>
<string name="action_edit_profile">编辑个人资料</string>
<string name="action_edit_own_profile">编辑</string>
<string name="action_undo">撤销</string>
<string name="action_accept">接受</string>
<string name="action_reject">拒绝</string>
@ -104,10 +115,14 @@
<string name="send_status_link_to">分享链接到…</string>
<string name="send_status_content_to">分享嘟文到…</string>
<string name="send_media_to">分享媒体到…</string>
<string name="confirmation_reported">报告已发送!</string>
<string name="confirmation_unblocked">用户已被屏蔽</string>
<string name="confirmation_unmuted">用户已被隐藏</string>
<string name="confirmation_unblocked">已解除屏蔽</string>
<string name="confirmation_unmuted">已取消隐藏</string>
<string name="status_sent">已发送!</string>
<string name="status_sent_long">成功发布回复。</string>
<string name="hint_domain">域名</string>
<string name="hint_compose">有什么新鲜事?</string>
@ -118,6 +133,7 @@
<string name="search_no_results">没找到结果</string>
<string name="label_quick_reply">回复…</string>
<string name="label_avatar">头像</string>
<string name="label_header">标题</string>
@ -125,15 +141,16 @@
<string name="login_connection">正在连接…</string>
<string name="dialog_whats_an_instance">请输入你帐号所在的 Mastodon 站点的域名,比如 mastodon.socialpawoo.netmstdn.jpmao.daizhige.org<a href="https://instances.social">等等</a>
\n\n还没有 Mastodon 帐号?你也可以输入想注册的实例的域名,然后在该实例创建新的帐号并授权 Tusky 登入。
\n\n你帐号所在的 Mastodon 站点被称为 Mastodon 的一个「实例」instance。但是在 Mastodon 里,跨实例互动和站内互动一样简单。
\n\n可以前往 <a href="https://joinmastodon.org">https://joinmastodon.org</a> 了解更多信息。
<string name="dialog_whats_an_instance">请输入你帐号所在的 Mastodon 站点的域名,比如 pawoo.netmstdn.jpmao.daizhige.org<a href="https://instances.social">等等</a>
\n\n还没有 Mastodon 帐号?你也可以输入想注册的 Mastodon 站点的域名,然后在该服务器创建新的帐号并授权 Tusky 登入。
\n\n在 Mastodon 里,跨站互动和站内互动一样简单。可以前往 <a href="https://joinmastodon.org">https://joinmastodon.org</a> 了解更多信息。
</string>
<string name="dialog_title_finishing_media_upload">正在结束上传…</string>
<string name="dialog_message_uploading_media">正在上传…</string>
<string name="dialog_download_image">下载</string>
<string name="dialog_message_cancel_follow_request">移除关注请求?</string>
<string name="dialog_unfollow_warning">不再关注此用户?</string>
<string name="dialog_delete_toot_warning">删除这条嘟文?</string>
<string name="visibility_public">公开:所有人可见,并会出现在公共时间轴上</string>
<string name="visibility_unlisted">不公开:所有人可见,但不会出现在公共时间轴上</string>
@ -144,7 +161,6 @@
<string name="pref_title_edit_notification_settings">通知设置</string>
<string name="pref_title_notifications_enabled">通知</string>
<string name="pref_summary_notifications">帐号 %1$s</string>
<string name="pref_title_pull_notification_check_interval">检查间隔时间</string>
<string name="pref_title_notification_alerts">提醒</string>
<string name="pref_title_notification_alert_sound">通知铃声</string>
<string name="pref_title_notification_alert_vibrate">振动</string>
@ -156,6 +172,7 @@
<string name="pref_title_notification_filter_favourites">嘟文被收藏</string>
<string name="pref_title_appearance_settings">外观</string>
<string name="pref_title_app_theme">应用主题</string>
<string name="pref_title_timelines">时间线</string>
<string-array name="app_theme_names">
<item>黑夜</item>
@ -167,7 +184,7 @@
<string name="pref_title_browser_settings">浏览器</string>
<string name="pref_title_custom_tabs">使用 Chrome Custom Tabs</string>
<string name="pref_title_hide_follow_button">浏览时自动隐藏发嘟按钮</string>
<string name="pref_title_status_filter">时间过滤</string>
<string name="pref_title_status_filter">时间线过滤</string>
<string name="pref_title_status_tabs">选项卡</string>
<string name="pref_title_show_boosts">显示转嘟</string>
<string name="pref_title_show_replies">显示回复</string>
@ -179,18 +196,9 @@
<string name="pref_title_http_proxy_server">HTTP 代理服务器</string>
<string name="pref_title_http_proxy_port">HTTP 代理端口</string>
<string-array name="pull_notification_check_interval_names">
<item>15 分钟</item>
<item>20 分钟</item>
<item>25 分钟</item>
<item>30 分钟</item>
<item>45 分钟</item>
<item>1 小时</item>
<item>2 小时</item>
</string-array>
<string name="pref_default_post_privacy">嘟文默认可见范围</string>
<string name="pref_publishing">发布</string>
<string name="pref_failed_to_sync">同步失败设置</string>
<string-array name="post_privacy_names">
<item>公开</item>
@ -200,11 +208,21 @@
<string name="pref_status_text_size">字体大小</string>
<!--<string-array name="status_text_size_names">-->
<!--<item>小</item>-->
<!--<item>中</item>-->
<!--<item>大</item>-->
<!--</string-array>-->
<string-array name="status_text_size_names">
<item>最小</item>
<item></item>
<item>标准</item>
<item></item>
<item>最大</item>
</string-array>
<string name="pref_media_display">媒体显示</string>
<string-array name="media_display_names">
<item>自动隐藏敏感媒体</item>
<item>总是显示敏感媒体</item>
<item>总是隐藏所有媒体</item>
</string-array>
<string name="notification_channel_mention_name">被提及</string>
<string name="notification_channel_mention_descriptions">当有用户在嘟文中提及我时</string>
@ -270,7 +288,7 @@
<string name="action_lists">列表</string>
<string name="title_lists">列表</string>
<string name="title_list_timeline">列表公共时间</string>
<string name="title_list_timeline">列表公共时间线</string>
<string name="compose_active_account_description">使用帐号 %1$s 发布嘟文</string>
@ -280,10 +298,52 @@
<string name="lock_account_label">保护你的帐户(锁嘟)</string>
<string name="lock_account_label_description">你需要手动审核所有关注请求</string>
<string name="compose_save_draft">保存为草稿?</string>
<string name="send_toot_notification_title">正在发送...</string>
<string name="send_toot_notification_title">正在发送</string>
<string name="send_toot_notification_error_title">发送失败</string>
<string name="send_toot_notification_channel_name">嘟文发送中</string>
<string name="send_toot_notification_cancel_title">发送已被取消</string>
<string name="send_toot_notification_saved_content">嘟文已保存到草稿</string>
<string name="action_compose_shortcut">发表嘟文</string>
<string name="error_no_custom_emojis">当前服务器没有自定义的 Emoji 表情</string>
<string name="copy_to_clipboard_success">已复制到剪贴板</string>
<string name="emoji_style">Emoji 表情</string>
<string name="system_default">系统默认</string>
<string name="download_fonts">需要下载 Emoji 数据</string>
<string name="performing_lookup_title">正在查找…</string>
<string name="expand_collapse_all_statuses">展开/折叠所有嘟文</string>
<string name="action_open_toot">打开嘟文</string>
<string name="restart_required">需要重启应用</string>
<string name="restart_emoji">你需要重启 Tusky 才能生效</string>
<string name="later">以后</string>
<string name="restart">立即重启</string>
<string name="caption_systememoji">系统默认 Emoji 表情</string>
<string name="caption_blobmoji">Android 4.47.1 的黄馒头 Emoji 表情</string>
<string name="caption_twemoji">Mastodon 使用的 Emoji 表情</string>
<string name="download_failed">下载失败</string>
<string name="profile_badge_bot_text">Bot</string>
<string name="account_moved_description">%1$s 已迁移到:</string>
<string name="reblog_private">转嘟(可见者不变)</string>
<string name="unreblog_private">取消转嘟</string>
<string name="license_description">Tusky 使用了以下开源项目的源码:</string>
<string name="license_apache_2">以 Apache License 授权(复制如下)</string>
<string name="license_cc_by_4">CC-BY 4.0</string>
<string name="license_cc_by_sa_4">CC-BY-SA 4.0</string>
<string name="profile_metadata_label">个人资料附加信息</string>
<string name="profile_metadata_add">添加信息</string>
<string name="profile_metadata_label_label">标签</string>
<string name="profile_metadata_content_label">内容</string>
<string name="pref_title_absolute_time">嘟文显示精确时间</string>
<string name="label_remote_account">以下信息可能并不完整,要查看完整资料请使用浏览器打开。</string>
<string name="unpin_action">取消置顶</string>
<string name="pin_action">置顶</string>
</resources>

View File

@ -3,13 +3,14 @@
<string name="error_generic">應用程式出現異常。</string>
<string name="error_empty">內容不能為空。</string>
<string name="error_invalid_domain">該域名無效</string>
<string name="error_failed_app_registration">無法連接到該實例</string>
<string name="error_failed_app_registration">無法連接此伺服器</string>
<string name="error_no_web_browser_found">沒有可用的瀏覽器。</string>
<string name="error_authorization_unknown">認證過程出現未知錯誤。</string>
<string name="error_authorization_denied">授權被拒絕。</string>
<string name="error_retrieving_oauth_token">無法獲取登錄信息。</string>
<string name="error_compose_character_limit">嘟文太長了!</string>
<string name="error_image_upload_size">文件大小限制 8MB。</string>
<string name="error_video_upload_size">視頻文件大小限制 40MB。</string>
<string name="error_media_upload_type">無法上傳此類型的文件。</string>
<string name="error_media_upload_opening">此文件無法打開。</string>
<string name="error_media_upload_permission">需要授予 Tusky 讀取媒體文件的權限。</string>
@ -18,15 +19,17 @@
<string name="error_media_upload_sending">媒體文件上傳失敗。</string>
<string name="error_report_too_few_statuses">至少要報告 1 則嘟文。</string>
<string name="error_invalid_regex">正規表示式無效</string>
<string name="error_sender_account_gone">嘟文發送時出錯。</string>
<string name="title_home">主頁</string>
<string name="title_advanced">高級</string>
<string name="title_notifications">通知</string>
<string name="title_public_local">本站時間軸</string>
<string name="title_public_federated">跨站公共時間</string>
<string name="title_public_federated">跨站公共時間</string>
<string name="title_view_thread">嘟文</string>
<string name="title_tag">#%s</string>
<string name="title_statuses">嘟文</string>
<string name="title_statuses_with_replies">嘟文和回覆</string>
<string name="title_follows">正在關注</string>
<string name="title_followers">關注者</string>
<string name="title_favourites">我的收藏</string>
@ -35,6 +38,7 @@
<string name="title_follow_requests">關注請求</string>
<string name="title_edit_profile">編輯個人資料</string>
<string name="title_saved_toot">草稿</string>
<string name="title_licenses">開源授權</string>
<string name="status_username_format">\@%s</string>
<string name="status_boosted_format">%s 轉嘟了</string>
@ -43,6 +47,8 @@
<string name="status_sensitive_media_directions">點擊顯示</string>
<string name="status_content_warning_show_more">顯示更多</string>
<string name="status_content_warning_show_less">摺疊內容</string>
<string name="status_content_show_less">摺疊</string>
<string name="status_content_show_more">展開</string>
<string name="footer_empty">還沒有內容,向下滑動即可刷新。</string>
@ -53,18 +59,21 @@
<string name="report_username_format">報告用戶 @%s 的濫用行為</string>
<string name="report_comment_hint">報告更多信息</string>
<string name="action_quick_reply">快捷回覆</string>
<string name="action_reply">回覆</string>
<string name="action_reblog">轉嘟</string>
<string name="action_favourite">收藏</string>
<string name="action_more">更多</string>
<string name="action_compose">發表新嘟文</string>
<string name="action_login">登錄 Mastodon 帳號</string>
<string name="action_logout">登出</string>
<string name="action_logout_confirm">確定要出帳號 %1$s 嗎?</string>
<string name="action_logout">退出登錄</string>
<string name="action_logout_confirm">確定要退出帳號 %1$s 嗎?</string>
<string name="action_follow">關注</string>
<string name="action_unfollow">取消關注</string>
<string name="action_block">屏蔽</string>
<string name="action_unblock">取消屏蔽</string>
<string name="action_hide_reblogs">隱藏轉嘟</string>
<string name="action_show_reblogs">顯示轉嘟</string>
<string name="action_report">報告</string>
<string name="action_delete">刪除</string>
<string name="action_send">發嘟</string>
@ -73,6 +82,7 @@
<string name="action_close">關閉</string>
<string name="action_view_profile">個人資料</string>
<string name="action_view_preferences">設置</string>
<string name="action_view_account_preferences">帳戶設置</string>
<string name="action_view_favourites">我的收藏</string>
<string name="action_view_mutes">被隱藏的用戶</string>
<string name="action_view_blocks">被屏蔽的用戶</string>
@ -89,10 +99,11 @@
<string name="action_open_drawer">打開應用抽屜</string>
<string name="action_save">保存</string>
<string name="action_edit_profile">編輯個人資料</string>
<string name="action_edit_own_profile">編輯</string>
<string name="action_undo">撤銷</string>
<string name="action_accept">接受</string>
<string name="action_reject">拒絕</string>
<string name="action_search"></string>
<string name="action_search"></string>
<string name="action_access_saved_toot">草稿</string>
<string name="action_toggle_visibility">設置嘟文可見範圍</string>
<string name="action_content_warning">設置內容提醒信息</string>
@ -104,10 +115,14 @@
<string name="send_status_link_to">分享鏈接到…</string>
<string name="send_status_content_to">分享嘟文到…</string>
<string name="send_media_to">分享媒體到…</string>
<string name="confirmation_reported">報告已發送!</string>
<string name="confirmation_unblocked">用戶已被屏蔽</string>
<string name="confirmation_unmuted">用戶已被隱藏</string>
<string name="confirmation_unblocked">已解除屏蔽</string>
<string name="confirmation_unmuted">已取消隱藏</string>
<string name="status_sent">已發送!</string>
<string name="status_sent_long">成功發佈回覆。</string>
<string name="hint_domain">域名</string>
<string name="hint_compose">有什麼新鮮事?</string>
@ -118,6 +133,7 @@
<string name="search_no_results">沒找到結果</string>
<string name="label_quick_reply">回覆…</string>
<string name="label_avatar">頭像</string>
<string name="label_header">標題</string>
@ -125,15 +141,16 @@
<string name="login_connection">正在連接…</string>
<string name="dialog_whats_an_instance">請輸入你帳號所在的 Mastodon 站點的域名,比如 mastodon.socialpawoo.netmstdn.jpmao.daizhige.org<a href="https://instances.social">等等</a>
\n\n還沒有 Mastodon 帳號?你也可以輸入想註冊的實例的域名,然後在該實例創建新的帳號並授權 Tusky 登入。
\n\n你帳號所在的 Mastodon 站點被稱為 Mastodon 的一個「實例」instance。但是在 Mastodon 裡,跨實例互動和站內互動一樣簡單。
\n\n可以前往 <a href="https://joinmastodon.org">https://joinmastodon.org</a> 瞭解更多信息。
<string name="dialog_whats_an_instance">請輸入你帳號所在的 Mastodon 站點的域名,比如 pawoo.netmstdn.jpmao.daizhige.org<a href="https://instances.social">等等</a>
\n\n還沒有 Mastodon 帳號?你也可以輸入想註冊的 Mastodon 站點的域名,然後在該伺服器創建新的帳號並授權 Tusky 登入。
\n\n在 Mastodon 裡,跨站互動和站內互動一樣簡單。可以前往 <a href="https://joinmastodon.org">https://joinmastodon.org</a> 瞭解更多信息。
</string>
<string name="dialog_title_finishing_media_upload">正在結束上傳…</string>
<string name="dialog_message_uploading_media">正在上傳…</string>
<string name="dialog_download_image">下載</string>
<string name="dialog_message_cancel_follow_request">移除關注請求?</string>
<string name="dialog_unfollow_warning">不再關注此用戶?</string>
<string name="dialog_delete_toot_warning">刪除這條嘟文?</string>
<string name="visibility_public">公開:所有人可見,並會出現在公共時間軸上</string>
<string name="visibility_unlisted">不公開:所有人可見,但不會出現在公共時間軸上</string>
@ -144,7 +161,6 @@
<string name="pref_title_edit_notification_settings">通知設置</string>
<string name="pref_title_notifications_enabled">通知</string>
<string name="pref_summary_notifications">帳號 %1$s</string>
<string name="pref_title_pull_notification_check_interval">檢查間隔時間</string>
<string name="pref_title_notification_alerts">提醒</string>
<string name="pref_title_notification_alert_sound">通知鈴聲</string>
<string name="pref_title_notification_alert_vibrate">振動</string>
@ -156,6 +172,7 @@
<string name="pref_title_notification_filter_favourites">嘟文被收藏</string>
<string name="pref_title_appearance_settings">外觀</string>
<string name="pref_title_app_theme">應用主題</string>
<string name="pref_title_timelines">時間線</string>
<string-array name="app_theme_names">
<item>黑夜</item>
@ -167,7 +184,7 @@
<string name="pref_title_browser_settings">瀏覽器</string>
<string name="pref_title_custom_tabs">使用 Chrome Custom Tabs</string>
<string name="pref_title_hide_follow_button">瀏覽時自動隱藏發嘟按鈕</string>
<string name="pref_title_status_filter">時間過濾</string>
<string name="pref_title_status_filter">時間過濾</string>
<string name="pref_title_status_tabs">選項卡</string>
<string name="pref_title_show_boosts">顯示轉嘟</string>
<string name="pref_title_show_replies">顯示回覆</string>
@ -176,21 +193,12 @@
<string name="pref_title_proxy_settings">代理</string>
<string name="pref_title_http_proxy_settings">HTTP 代理</string>
<string name="pref_title_http_proxy_enable">啟用 HTTP 代理</string>
<string name="pref_title_http_proxy_server">HTTP 代理服</string>
<string name="pref_title_http_proxy_server">HTTP 代理服器</string>
<string name="pref_title_http_proxy_port">HTTP 代理端口</string>
<string-array name="pull_notification_check_interval_names">
<item>15 分鐘</item>
<item>20 分鐘</item>
<item>25 分鐘</item>
<item>30 分鐘</item>
<item>45 分鐘</item>
<item>1 小時</item>
<item>2 小時</item>
</string-array>
<string name="pref_default_post_privacy">嘟文默認可見範圍</string>
<string name="pref_publishing">發佈</string>
<string name="pref_failed_to_sync">同步失敗設置</string>
<string-array name="post_privacy_names">
<item>公開</item>
@ -200,11 +208,21 @@
<string name="pref_status_text_size">字體大小</string>
<!--<string-array name="status_text_size_names">-->
<!--<item>小</item>-->
<!--<item>中</item>-->
<!--<item>大</item>-->
<!--</string-array>-->
<string-array name="status_text_size_names">
<item>最小</item>
<item></item>
<item>標準</item>
<item></item>
<item>最大</item>
</string-array>
<string name="pref_media_display">媒體顯示</string>
<string-array name="media_display_names">
<item>自動隱藏敏感媒體</item>
<item>總是顯示敏感媒體</item>
<item>總是隱藏所有媒體</item>
</string-array>
<string name="notification_channel_mention_name">被提及</string>
<string name="notification_channel_mention_descriptions">當有用戶在嘟文中提及我時</string>
@ -270,7 +288,7 @@
<string name="action_lists">列表</string>
<string name="title_lists">列表</string>
<string name="title_list_timeline">列表公共時間</string>
<string name="title_list_timeline">列表公共時間</string>
<string name="compose_active_account_description">使用帳號 %1$s 發佈嘟文</string>
@ -280,10 +298,52 @@
<string name="lock_account_label">保護你的帳戶(鎖嘟)</string>
<string name="lock_account_label_description">你需要手動審核所有關注請求</string>
<string name="compose_save_draft">保存為草稿?</string>
<string name="send_toot_notification_title">正在發送...</string>
<string name="send_toot_notification_title">正在發送</string>
<string name="send_toot_notification_error_title">發送失敗</string>
<string name="send_toot_notification_channel_name">嘟文發送中</string>
<string name="send_toot_notification_cancel_title">發送已被取消</string>
<string name="send_toot_notification_saved_content">嘟文已保存到草稿</string>
<string name="action_compose_shortcut">發表嘟文</string>
<string name="error_no_custom_emojis">當前服務器沒有自定義的 Emoji 表情</string>
<string name="copy_to_clipboard_success">已複製到剪貼板</string>
<string name="emoji_style">Emoji 表情</string>
<string name="system_default">系統默認</string>
<string name="download_fonts">需要下載 Emoji 數據</string>
<string name="performing_lookup_title">正在查找…</string>
<string name="expand_collapse_all_statuses">展開/摺疊所有嘟文</string>
<string name="action_open_toot">打開嘟文</string>
<string name="restart_required">需要重啟應用</string>
<string name="restart_emoji">你需要重啟 Tusky 才能生效</string>
<string name="later">以後</string>
<string name="restart">立即重啟</string>
<string name="caption_systememoji">系統默認 Emoji 表情</string>
<string name="caption_blobmoji">Android 4.47.1 的黃饅頭 Emoji 表情</string>
<string name="caption_twemoji">Mastodon 使用的 Emoji 表情</string>
<string name="download_failed">下載失敗</string>
<string name="profile_badge_bot_text">Bot</string>
<string name="account_moved_description">%1$s 已遷移到:</string>
<string name="reblog_private">轉嘟(可見者不變)</string>
<string name="unreblog_private">取消轉嘟</string>
<string name="license_description">Tusky 使用了以下開源項目的源碼:</string>
<string name="license_apache_2">以 Apache License 授權(複製如下)</string>
<string name="license_cc_by_4">CC-BY 4.0</string>
<string name="license_cc_by_sa_4">CC-BY-SA 4.0</string>
<string name="profile_metadata_label">个人资料附加信息</string>
<string name="profile_metadata_add">添加信息</string>
<string name="profile_metadata_label_label">標籤</string>
<string name="profile_metadata_content_label">內容</string>
<string name="pref_title_absolute_time">嘟文顯示精確時間</string>
<string name="label_remote_account">以下信息可能並不完整,要查看完整資料請使用瀏覽器打開。</string>
<string name="unpin_action">取消置頂</string>
<string name="pin_action">置頂</string>
</resources>

View File

@ -30,4 +30,5 @@
<item name="wrap_content" format="integer" type="dimen">-2</item>
<dimen name="preference_icon_size">20dp</dimen>
</resources>

View File

@ -8,16 +8,6 @@
<string name="status_sensitive_media_template" translatable="false">&lt;b>%1$s&lt;/b>&lt;br>%2$s</string>
<string-array name="pull_notification_check_intervals" inputType="integer">
<item>15</item>
<item>20</item>
<item>25</item>
<item>30</item>
<item>45</item>
<item>60</item>
<item>120</item>
</string-array>
<string-array name="post_privacy_values">
<item>public</item>
<item>unlisted</item>

View File

@ -82,6 +82,7 @@
<string name="action_close">Close</string>
<string name="action_view_profile">Profile</string>
<string name="action_view_preferences">Preferences</string>
<string name="action_view_account_preferences">Account Preferences</string>
<string name="action_view_favourites">Favourites</string>
<string name="action_view_mutes">Muted users</string>
<string name="action_view_blocks">Blocked users</string>
@ -162,10 +163,9 @@
<string name="visibility_direct">Direct: Post to mentioned users only</string>
<string name="pref_title_notification_settings">Notifications</string>
<string name="pref_title_edit_notification_settings">Edit Notifications</string>
<string name="pref_title_edit_notification_settings">Notifications</string>
<string name="pref_title_notifications_enabled">Notifications</string>
<string name="pref_summary_notifications">for account %1$s</string>
<string name="pref_title_pull_notification_check_interval">Check Interval</string>
<string name="pref_title_notification_alerts">Alerts</string>
<string name="pref_title_notification_alert_sound">Notify with a sound</string>
<string name="pref_title_notification_alert_vibrate">Notify with vibration</string>
@ -177,6 +177,7 @@
<string name="pref_title_notification_filter_favourites">my posts are favourited</string>
<string name="pref_title_appearance_settings">Appearance</string>
<string name="pref_title_app_theme">App Theme</string>
<string name="pref_title_timelines">Timelines</string>
<string-array name="app_theme_names">
<item>Dark</item>
@ -200,18 +201,9 @@
<string name="pref_title_http_proxy_server">HTTP proxy server</string>
<string name="pref_title_http_proxy_port">HTTP proxy port</string>
<string-array name="pull_notification_check_interval_names">
<item>15 minutes</item>
<item>20 minutes</item>
<item>25 minutes</item>
<item>30 minutes</item>
<item>45 minutes</item>
<item>1 hour</item>
<item>2 hours</item>
</string-array>
<string name="pref_default_post_privacy">Default post privacy</string>
<string name="pref_publishing">Publishing</string>
<string name="pref_publishing">Publishing (synced with server)</string>
<string name="pref_failed_to_sync">Failed to sync settings</string>
<string-array name="post_privacy_names">
<item>Public</item>
@ -229,6 +221,14 @@
<item>Largest</item>
</string-array>
<string name="pref_media_display">Media display</string>
<string-array name="media_display_names">
<item>Hide media marked as sensitive</item>
<item>Always show media marked as sensitive</item>
<item>Always hide all media</item>
</string-array>
<string name="notification_channel_mention_name">New Mentions</string>
<string name="notification_channel_mention_descriptions">Notifications about new mentions</string>
<string name="notification_channel_follow_name">New Followers</string>

View File

@ -127,6 +127,8 @@
<item name="compound_button_color">@color/compound_button_color_light</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>
<style name="AppTheme.ImageButton.Light" parent="Widget.AppCompat.Button.Borderless.Colored">

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="@string/preferences_file_key">
<Preference
android:key="notificationPreference"
android:title="@string/pref_title_edit_notification_settings" />
<Preference
android:key="mutedUsersPreference"
android:title="@string/action_view_mutes" />
<Preference
android:key="blockedUsersPreference"
android:title="@string/action_view_blocks" />
<PreferenceCategory android:title="@string/pref_publishing">
<ListPreference
android:defaultValue="public"
android:entries="@array/post_privacy_names"
android:entryValues="@array/post_privacy_values"
android:key="defaultPostPrivacy"
android:summary="%s"
android:title="@string/pref_default_post_privacy" />
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_eye_24dp"
android:key="defaultMediaSensitivity"
android:title="Always mark media as sensitive" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_title_timelines">
<SwitchPreference
android:key="mediaPreviewEnabled"
android:title="@string/pref_title_show_media_preview" />
<SwitchPreference
android:key="alwaysShowSensitiveMedia"
android:title="@string/pref_title_alway_show_sensitive_media" />
</PreferenceCategory>
</PreferenceScreen>

View File

@ -1,18 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen android:title="@string/pref_title_http_proxy_settings"
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/pref_title_http_proxy_settings">
<CheckBoxPreference
<SwitchPreference
android:defaultValue="false"
android:key="httpProxyEnabled"
android:title="@string/pref_title_http_proxy_enable" />
android:title="@string/pref_title_http_proxy_enable"
app:iconSpaceReserved="false" />
<EditTextPreference
android:key="httpProxyServer"
android:summary="%s"
android:title="@string/pref_title_http_proxy_server"
android:summary="%s" />
app:iconSpaceReserved="false" />
<EditTextPreference
android:key="httpProxyPort"
android:summary="%s"
android:title="@string/pref_title_http_proxy_port"
android:summary="%s" />
app:iconSpaceReserved="false" />
</PreferenceScreen>

View File

@ -1,57 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen android:key="notificationSettings"
android:title="@string/pref_title_edit_notification_settings"
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:key="notificationSettings"
android:title="@string/pref_title_edit_notification_settings">
<CheckBoxPreference
<SwitchPreference
android:defaultValue="true"
android:key="notificationsEnabled"
android:title="@string/pref_title_notifications_enabled" />
android:title="@string/pref_title_notifications_enabled"
app:iconSpaceReserved="false" />
<PreferenceCategory
android:dependency="notificationsEnabled"
android:title="@string/pref_title_notification_filters">
android:title="@string/pref_title_notification_filters"
app:iconSpaceReserved="false">
<CheckBoxPreference
<SwitchPreference
android:defaultValue="true"
android:key="notificationFilterMentions"
android:title="@string/pref_title_notification_filter_mentions" />
android:title="@string/pref_title_notification_filter_mentions"
app:iconSpaceReserved="false" />
<CheckBoxPreference
<SwitchPreference
android:defaultValue="true"
android:key="notificationFilterFollows"
android:title="@string/pref_title_notification_filter_follows" />
android:title="@string/pref_title_notification_filter_follows"
app:iconSpaceReserved="false" />
<CheckBoxPreference
<SwitchPreference
android:defaultValue="true"
android:key="notificationFilterReblogs"
android:title="@string/pref_title_notification_filter_reblogs" />
android:title="@string/pref_title_notification_filter_reblogs"
app:iconSpaceReserved="false" />
<CheckBoxPreference
<SwitchPreference
android:defaultValue="true"
android:key="notificationFilterFavourites"
android:title="@string/pref_title_notification_filter_favourites" />
android:title="@string/pref_title_notification_filter_favourites"
app:iconSpaceReserved="false" />
</PreferenceCategory>
<PreferenceCategory
android:dependency="notificationsEnabled"
android:title="@string/pref_title_notification_alerts">
android:title="@string/pref_title_notification_alerts"
app:iconSpaceReserved="false">
<CheckBoxPreference
<SwitchPreference
android:defaultValue="true"
android:key="notificationAlertSound"
android:title="@string/pref_title_notification_alert_sound" />
android:title="@string/pref_title_notification_alert_sound"
app:iconSpaceReserved="false" />
<CheckBoxPreference
<SwitchPreference
android:defaultValue="true"
android:key="notificationAlertVibrate"
android:title="@string/pref_title_notification_alert_vibrate" />
android:title="@string/pref_title_notification_alert_vibrate"
app:iconSpaceReserved="false" />
<CheckBoxPreference
<SwitchPreference
android:defaultValue="true"
android:key="notificationAlertLight"
android:title="@string/pref_title_notification_alert_light" />
android:title="@string/pref_title_notification_alert_light"
app:iconSpaceReserved="false" />
</PreferenceCategory>

View File

@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:key="@string/preferences_file_key">
<PreferenceCategory android:title="@string/pref_title_appearance_settings">
<PreferenceCategory
android:title="@string/pref_title_appearance_settings">
<ListPreference
android:defaultValue="night"
@ -16,7 +18,8 @@
android:defaultValue="@string/system_default"
android:key="emojiCompat"
android:summary="@string/system_default"
android:title="@string/emoji_style"/>
android:icon="@drawable/ic_emoji_24dp"
android:title="@string/emoji_style" />
<ListPreference
android:defaultValue="medium"
@ -31,64 +34,31 @@
android:key="fabHide"
android:title="@string/pref_title_hide_follow_button" />
<CheckBoxPreference
android:defaultValue="true"
android:key="mediaPreviewEnabled"
android:title="@string/pref_title_show_media_preview" />
<CheckBoxPreference
android:defaultValue="false"
android:dependency="mediaPreviewEnabled"
android:key="alwaysShowSensitiveMedia"
android:title="@string/pref_title_alway_show_sensitive_media" />
<CheckBoxPreference
android:defaultValue="false"
android:key="absoluteTimeView"
android:title="@string/pref_title_absolute_time" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_publishing">
<ListPreference
android:defaultValue="public"
android:entries="@array/post_privacy_names"
android:entryValues="@array/post_privacy_values"
android:key="defaultPostPrivacy"
android:summary="%s"
android:title="@string/pref_default_post_privacy" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_title_browser_settings">
<PreferenceCategory
android:title="@string/pref_title_browser_settings">
<CheckBoxPreference
android:defaultValue="false"
android:key="customTabs"
android:title="@string/pref_title_custom_tabs" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_title_status_filter">
<PreferenceCategory
android:title="@string/pref_title_status_filter">
<Preference
android:key="timelineFilterPreferences"
android:title="@string/pref_title_status_tabs" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_title_notification_settings">
<ListPreference
android:defaultValue="15"
android:entries="@array/pull_notification_check_interval_names"
android:entryValues="@array/pull_notification_check_intervals"
android:key="pullNotificationCheckInterval"
android:summary="%s"
android:title="@string/pref_title_pull_notification_check_interval" />
<Preference
android:key="notificationPreferences"
android:title="@string/pref_title_edit_notification_settings" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_title_proxy_settings">
<PreferenceCategory
android:title="@string/pref_title_proxy_settings">
<Preference
android:key="httpProxyPreferences"
android:summary="%s"

View File

@ -1,23 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen android:title="@string/pref_title_status_tabs"
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/pref_title_status_tabs">
<PreferenceCategory android:title="@string/title_home">
<PreferenceCategory
android:title="@string/title_home"
app:iconSpaceReserved="false">
<CheckBoxPreference
android:defaultValue="true"
android:key="tabFilterHomeBoosts"
android:title="@string/pref_title_show_boosts" />
android:title="@string/pref_title_show_boosts"
app:iconSpaceReserved="false" />
<CheckBoxPreference
android:defaultValue="true"
android:key="tabFilterHomeReplies"
android:title="@string/pref_title_show_replies" />
android:title="@string/pref_title_show_replies"
app:iconSpaceReserved="false" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/title_advanced">
<EditTextPreference
android:key="tabFilterRegex"
<PreferenceCategory
android:title="@string/title_advanced"
app:iconSpaceReserved="false">
<Preference
android:inputType="textNoSuggestions"
android:title="@string/pref_title_filter_regex" />
android:key="tabFilterRegex"
android:title="@string/pref_title_filter_regex"
app:iconSpaceReserved="false" />
</PreferenceCategory>
</PreferenceScreen>