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
commit 348c20c792
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
65 changed files with 1636 additions and 1083 deletions

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