From 348c20c792dc4c17b47a39773db34d7f9dce2328 Mon Sep 17 00:00:00 2001 From: Konrad Pozniak Date: Mon, 12 Nov 2018 21:09:39 +0100 Subject: [PATCH] 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. --- app/build.gradle | 1 + .../tusky/AccountListActivity.java | 2 +- .../com/keylesspalace/tusky/BaseActivity.java | 35 -- .../keylesspalace/tusky/ComposeActivity.java | 6 +- .../keylesspalace/tusky/EmojiPreference.java | 71 ++-- .../com/keylesspalace/tusky/MainActivity.java | 57 ++-- .../tusky/PreferencesActivity.java | 180 ----------- .../tusky/PreferencesActivity.kt | 186 +++++++++++ .../keylesspalace/tusky/TuskyApplication.java | 10 +- .../keylesspalace/tusky/appstore/Events.kt | 3 +- .../keylesspalace/tusky/db/AccountEntity.kt | 5 + .../keylesspalace/tusky/db/AccountManager.kt | 3 + .../keylesspalace/tusky/db/AppDatabase.java | 13 +- .../com/keylesspalace/tusky/db/Converters.kt | 11 + .../tusky/di/ActivitiesModule.kt | 4 +- .../tusky/di/FragmentBuildersModule.kt | 8 + .../tusky/fragment/NotificationsFragment.java | 19 +- .../tusky/fragment/PreferencesFragment.java | 303 ------------------ .../tusky/fragment/TimelineFragment.java | 21 +- .../tusky/fragment/ViewThreadFragment.java | 4 +- .../preference/AccountPreferencesFragment.kt | 252 +++++++++++++++ .../NotificationPreferencesFragment.kt | 114 +++++++ .../preference/PreferencesFragment.kt | 101 ++++++ .../preference/ProxyPreferencesFragment.kt | 76 +++++ .../TabFilterPreferencesFragment.kt | 88 +++++ .../tusky/network/MastodonApi.java | 5 + .../tusky/util/NotificationHelper.java | 28 ++ .../NotificationPullJobCreator.java | 7 +- .../tusky/util/SharedPreferencesExtensions.kt | 7 + .../main/res/drawable/ic_account_settings.xml | 13 + app/src/main/res/drawable/ic_logout.xml | 10 + app/src/main/res/drawable/ic_notebook.xml | 8 + app/src/main/res/values-ar/strings.xml | 1 - app/src/main/res/values-ca/strings.xml | 11 - app/src/main/res/values-cy/strings.xml | 11 - app/src/main/res/values-de/strings.xml | 11 - app/src/main/res/values-es/strings.xml | 11 - app/src/main/res/values-fa/strings.xml | 11 - app/src/main/res/values-fr/strings.xml | 11 - app/src/main/res/values-hu/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 11 - app/src/main/res/values-ja/strings.xml | 11 - app/src/main/res/values-ko/strings.xml | 1 - app/src/main/res/values-night/styles.xml | 2 + app/src/main/res/values-nl/strings.xml | 11 - app/src/main/res/values-oc/strings.xml | 11 - app/src/main/res/values-pl/strings.xml | 11 - app/src/main/res/values-pt-rBR/strings.xml | 12 - app/src/main/res/values-ru/strings.xml | 11 - app/src/main/res/values-sv/strings.xml | 11 - app/src/main/res/values-ta/strings.xml | 11 - app/src/main/res/values-zh-rCN/strings.xml | 118 +++++-- app/src/main/res/values-zh-rHK/strings.xml | 152 ++++++--- app/src/main/res/values-zh-rMO/strings.xml | 152 ++++++--- app/src/main/res/values-zh-rSG/strings.xml | 118 +++++-- app/src/main/res/values-zh-rTW/strings.xml | 122 +++++-- app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/donottranslate.xml | 10 - app/src/main/res/values/strings.xml | 26 +- app/src/main/res/values/styles.xml | 2 + app/src/main/res/xml/account_preferences.xml | 47 +++ .../main/res/xml/http_proxy_preferences.xml | 16 +- .../main/res/xml/notification_preferences.xml | 53 +-- app/src/main/res/xml/preferences.xml | 54 +--- .../res/xml/timeline_filter_preferences.xml | 26 +- 65 files changed, 1636 insertions(+), 1083 deletions(-) delete mode 100644 app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.java create mode 100644 app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt delete mode 100644 app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java create mode 100644 app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt create mode 100644 app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt create mode 100644 app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt create mode 100644 app/src/main/java/com/keylesspalace/tusky/fragment/preference/ProxyPreferencesFragment.kt create mode 100644 app/src/main/java/com/keylesspalace/tusky/fragment/preference/TabFilterPreferencesFragment.kt rename app/src/main/java/com/keylesspalace/tusky/{ => util}/NotificationPullJobCreator.java (97%) create mode 100644 app/src/main/java/com/keylesspalace/tusky/util/SharedPreferencesExtensions.kt create mode 100644 app/src/main/res/drawable/ic_account_settings.xml create mode 100644 app/src/main/res/drawable/ic_logout.xml create mode 100644 app/src/main/res/drawable/ic_notebook.xml create mode 100644 app/src/main/res/xml/account_preferences.xml diff --git a/app/build.gradle b/app/build.gradle index 02715f8e..2952d343 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountListActivity.java b/app/src/main/java/com/keylesspalace/tusky/AccountListActivity.java index 58c3e8f5..b6370df0 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountListActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/AccountListActivity.java @@ -52,7 +52,7 @@ public final class AccountListActivity extends BaseActivity implements HasSuppor return intent; } - enum Type { + public enum Type { BLOCKS, MUTES, FOLLOW_REQUESTS, diff --git a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java index 00685e13..c8e65e66 100644 --- a/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/BaseActivity.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java index b76ad7b1..cf693246 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/ComposeActivity.java @@ -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())); diff --git a/app/src/main/java/com/keylesspalace/tusky/EmojiPreference.java b/app/src/main/java/com/keylesspalace/tusky/EmojiPreference.java index fb1943da..e296604e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/EmojiPreference.java +++ b/app/src/main/java/com/keylesspalace/tusky/EmojiPreference.java @@ -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; - } + } } diff --git a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java index 730c1af3..525ec40a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/MainActivity.java +++ b/app/src/main/java/com/keylesspalace/tusky/MainActivity.java @@ -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 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 listItems = new ArrayList<>(11); + List 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) { diff --git a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.java b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.java deleted file mode 100644 index c4f002ef..00000000 --- a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.java +++ /dev/null @@ -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 . */ - -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); - } -} diff --git a/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt new file mode 100644 index 00000000..35900218 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/PreferencesActivity.kt @@ -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 . */ + +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 + + 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 { + 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 + } + } + +} diff --git a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java index 04ae5ab1..299d742e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java +++ b/app/src/main/java/com/keylesspalace/tusky/TuskyApplication.java @@ -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() { diff --git a/app/src/main/java/com/keylesspalace/tusky/appstore/Events.kt b/app/src/main/java/com/keylesspalace/tusky/appstore/Events.kt index cc70e5ce..db60148e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/appstore/Events.kt +++ b/app/src/main/java/com/keylesspalace/tusky/appstore/Events.kt @@ -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 \ No newline at end of file +data class ProfileEditedEvent(val newProfileData: Account) : Dispatchable +data class PreferenceChangedEvent(val preferenceKey: String) : Dispatchable \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt b/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt index 1585f43c..c52b8033 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/AccountEntity.kt @@ -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 = emptyList()) { diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AccountManager.kt b/app/src/main/java/com/keylesspalace/tusky/db/AccountManager.kt index c9832e09..c8098619 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/AccountManager.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/AccountManager.kt @@ -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) diff --git a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java index 8624d8d6..f07e7ad3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java +++ b/app/src/main/java/com/keylesspalace/tusky/db/AppDatabase.java @@ -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'"); + } + }; + } \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt b/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt index 76b7906b..7087940a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt +++ b/app/src/main/java/com/keylesspalace/tusky/db/Converters.kt @@ -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?): 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) + } } \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/di/ActivitiesModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/ActivitiesModule.kt index 2559bbd4..d51b314c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/ActivitiesModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/ActivitiesModule.kt @@ -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 diff --git a/app/src/main/java/com/keylesspalace/tusky/di/FragmentBuildersModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/FragmentBuildersModule.kt index 93adf2d0..d344edb5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/FragmentBuildersModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/FragmentBuildersModule.kt @@ -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 + } \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java index 7644e048..95c070a2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/NotificationsFragment.java @@ -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(); diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java deleted file mode 100644 index e36d7cb6..00000000 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/PreferencesFragment.java +++ /dev/null @@ -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 . */ - -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(""); - - } - } -} diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java index ec1ff1ef..881101a9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/TimelineFragment.java @@ -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; } } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java index 175419bb..23f97119 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java @@ -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); diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt new file mode 100644 index 00000000..86095104 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/AccountPreferencesFragment.kt @@ -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 . */ + +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{ + override fun onResponse(call: Call, response: Response) { + 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, 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() + } + } + +} diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt new file mode 100644 index 00000000..bed19fbe --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/NotificationPreferencesFragment.kt @@ -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 . */ + +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() + } + } + +} diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt new file mode 100644 index 00000000..35c646f6 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/PreferencesFragment.kt @@ -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 . */ + +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() + } + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/ProxyPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/ProxyPreferencesFragment.kt new file mode 100644 index 00000000..c562537b --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/ProxyPreferencesFragment.kt @@ -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 . */ + +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() + } + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/preference/TabFilterPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/TabFilterPreferencesFragment.kt new file mode 100644 index 00000000..dc86a94e --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/preference/TabFilterPreferencesFragment.kt @@ -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 . */ + +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() + } + } +} diff --git a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.java b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.java index 497a9745..a177a2e2 100644 --- a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.java +++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.java @@ -166,6 +166,11 @@ public interface MastodonApi { @GET("api/v1/accounts/verify_credentials") Call accountVerifyCredentials(); + @FormUrlEncoded + @PATCH("api/v1/accounts/update_credentials") + Call accountUpdateSource(@Nullable @Field("source[privacy]") String privacy, + @Nullable @Field("source[sensitive]") Boolean sensitive); + @Multipart @PATCH("api/v1/accounts/update_credentials") Call accountUpdateCredentials( diff --git a/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java b/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java index 7bf7010a..8bc23da9 100644 --- a/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/NotificationHelper.java @@ -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) { diff --git a/app/src/main/java/com/keylesspalace/tusky/NotificationPullJobCreator.java b/app/src/main/java/com/keylesspalace/tusky/util/NotificationPullJobCreator.java similarity index 97% rename from app/src/main/java/com/keylesspalace/tusky/NotificationPullJobCreator.java rename to app/src/main/java/com/keylesspalace/tusky/util/NotificationPullJobCreator.java index 0174d27c..69127ffb 100644 --- a/app/src/main/java/com/keylesspalace/tusky/NotificationPullJobCreator.java +++ b/app/src/main/java/com/keylesspalace/tusky/util/NotificationPullJobCreator.java @@ -13,7 +13,7 @@ * You should have received a copy of the GNU Lesser General Public License along with Tusky. If * not, see . */ -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); } } diff --git a/app/src/main/java/com/keylesspalace/tusky/util/SharedPreferencesExtensions.kt b/app/src/main/java/com/keylesspalace/tusky/util/SharedPreferencesExtensions.kt new file mode 100644 index 00000000..a3835a86 --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/util/SharedPreferencesExtensions.kt @@ -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 +} diff --git a/app/src/main/res/drawable/ic_account_settings.xml b/app/src/main/res/drawable/ic_account_settings.xml new file mode 100644 index 00000000..d13907dc --- /dev/null +++ b/app/src/main/res/drawable/ic_account_settings.xml @@ -0,0 +1,13 @@ + + + diff --git a/app/src/main/res/drawable/ic_logout.xml b/app/src/main/res/drawable/ic_logout.xml new file mode 100644 index 00000000..717009ad --- /dev/null +++ b/app/src/main/res/drawable/ic_logout.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_notebook.xml b/app/src/main/res/drawable/ic_notebook.xml new file mode 100644 index 00000000..2395cd14 --- /dev/null +++ b/app/src/main/res/drawable/ic_notebook.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index d09a488c..7ef6ea62 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -158,7 +158,6 @@ تعديل الاشعارات الإخطارات للحساب %1$s - مدة تحديث التحميل التنبيهات إعلام بالصوت إعلام بالاهتزار diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 453d31a4..ca41f4d7 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -139,7 +139,6 @@ Notificacions Edita les notificacions Notificacions - Comprova l\'interval Alertes Notifica amb un so Notifica amb una vibració @@ -159,16 +158,6 @@ Mostra les respostes Mostra les previsualitzacions - - 15 minuts - 20 minuts - 25 minuts - 30 minuts - 45 minuts - 1 hora - 2 hores - - Privacitat predeterminada dels toots Publicació diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml index bd7d5772..c557cffb 100644 --- a/app/src/main/res/values-cy/strings.xml +++ b/app/src/main/res/values-cy/strings.xml @@ -165,7 +165,6 @@ Golygu Hysbysiadau Hysbysiadau i gyfrif %1$s - Cadarnhau Amser Negeseuon pwysig Cael hysbysiad sŵn Cael hysbysiad crynu @@ -200,16 +199,6 @@ Gweinydd procsi HTTP Porthol procsiHTTP - - 15 munud - 20 munud - 25 munud - 30 munud - 45 munud - 1 awr - 2 awr - - Preifatrwydd postiadau rhagosodedig Cyhoeddi diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 6a6fb833..a086defa 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -134,16 +134,6 @@ Öffne Links in der App Verstecke Button bei Bildlauf - - 15 Minuten - 20 Minuten - 25 Minuten - 30 Minuten - 45 Minuten - 1 Stunde - 2 Stunden - - Öffentlich Nicht gelistet @@ -214,7 +204,6 @@ Zeige Antworten Zeige Medienvorschauen Zeige Boosts - Überprüfungsintervall leer Verstecke Medien Timeline-Filter diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index ac937007..edb2d413 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -160,7 +160,6 @@ Editar notificaciones Notificaciones para la cuenta %1$s - Intervalo Alertas Notificar con sonido Notificar con vibración @@ -195,16 +194,6 @@ Servidor del proxy HTTP Puerto del proxy HTTP - - 15 minutos - 20 minutos - 25 minutos - 30 minutos - 45 minutos - 1 hora - 2 hora - - Visibilidad por defecto Publicaciones diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 4c3c5e97..b5278891 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -165,7 +165,6 @@ ویرایش اعلان‌ها اعلان ها برای حساب %1$s - فاصله را بررسی کن هشدارها اعلان با صدا اعلان با لرزش @@ -199,16 +198,6 @@ فعالسازی پراکسی HTTP سرور پراکسی HTTP پورت پراکسی HTTP - - - ۱۵ دقیقه - ۲۰ دقیقه - ۲۵ دقیقه - ۳۰ دقیقه - ۴۵ دقیقه - ۱ ساعت - ۲ ساعت - حریم خصوصی پیشفرض پست انتشار diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index ada1bf1c..b7769eb9 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -177,7 +177,6 @@ Aucun résultat Désirez-vous cesser de suivre ce compte \? pour le compte %1$s - Interval de vérifications Theme de l\'application @@ -193,16 +192,6 @@ Adresse du serveur proxy HTTP Port du serveur proxy HTTP - - 15 minutes - 20 minutes - 25 minutes - 30 minutes - 45 minutes - 1 heure - 2 heures - - Confidentialité par defaut Publier diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index f0809fba..47d2ace1 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -140,7 +140,6 @@ Értesítések Értesítések szerkesztése Értesítések - Ellenőrzési időköz Figyelmeztetések Értesítés hanggal Értesítés rezgéssel diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 25d6c3ec..13c6548a 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -165,7 +165,6 @@ Modifica Notifiche Notifiche per l\'account %1$s - Intervallo di controllo Allarmi Notifica con suoneria Notifica con vibrazione @@ -200,16 +199,6 @@ Server proxy HTTP Porta proxy HTTP - - 15 minuti - 20 minuti - 25 minuti - 30 minuti - 45 minuti - 1 ora - 2 ore - - Privacy di default dei post Pubblicando diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 5701748e..53d11d60 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -159,7 +159,6 @@ 通知を設定 プッシュ通知 対象: %1$s - 通知の取得間隔 通知時の動作 着信音を鳴らす バイブレーションする @@ -194,16 +193,6 @@ HTTPプロキシーサーバー HTTPプロキシーポート - - 15分 - 20分 - 25分 - 30分 - 45分 - 1時間 - 2時間 - - デフォルトの投稿プライバシー 投稿 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index c762fad2..21953049 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -158,7 +158,6 @@ 알림 설정 편집 알림 %1$s 계정의 알림 - 확인 간격 알림 소리로 알림 진동으로 알림 diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index 9495c4dd..da0f3546 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -68,6 +68,8 @@ @color/compound_button_color_dark + @style/PreferenceThemeOverlay +