package com.battlelancer.seriesguide.ui; import android.app.Fragment; import android.app.FragmentTransaction; import android.app.backup.BackupManager; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; import android.preference.SwitchPreference; import android.provider.Settings; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StyleRes; import android.support.v4.app.TaskStackBuilder; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.MenuItem; import com.battlelancer.seriesguide.R; import com.battlelancer.seriesguide.appwidget.ListWidgetProvider; import com.battlelancer.seriesguide.backend.settings.HexagonSettings; import com.battlelancer.seriesguide.dataliberation.DataLiberationActivity; import com.battlelancer.seriesguide.provider.SeriesGuideContract.Episodes; import com.battlelancer.seriesguide.service.NotificationService; import com.battlelancer.seriesguide.settings.AdvancedSettings; import com.battlelancer.seriesguide.settings.AppSettings; import com.battlelancer.seriesguide.settings.DisplaySettings; import com.battlelancer.seriesguide.settings.NotificationSettings; import com.battlelancer.seriesguide.settings.TraktCredentials; import com.battlelancer.seriesguide.settings.UpdateSettings; import com.battlelancer.seriesguide.sync.SgSyncAdapter; import com.battlelancer.seriesguide.util.Shadows; import com.battlelancer.seriesguide.util.ThemeUtils; import com.battlelancer.seriesguide.util.Utils; import com.google.android.gms.analytics.GoogleAnalytics; /** * Allows tweaking of various SeriesGuide settings. Does NOT inherit from {@link * com.battlelancer.seriesguide.ui.BaseActivity} to avoid handling actions which might be confusing * while adjusting settings. */ public class SeriesGuidePreferences extends AppCompatActivity { private static final String EXTRA_SETTINGS_SCREEN = "settingsScreen"; private static final String TAG = "Settings"; // Preference keys private static final String KEY_CLEAR_CACHE = "clearCache"; public static final String KEY_OFFSET = "com.battlelancer.seriesguide.timeoffset"; public static final String KEY_DATABASEIMPORTED = "com.battlelancer.seriesguide.dbimported"; public static final String KEY_SECURE = "com.battlelancer.seriesguide.secure"; public static final String SUPPORT_MAIL = "support@seriesgui.de"; private static final String KEY_ABOUT = "aboutPref"; public static final String KEY_TAPE_INTERVAL = "com.battlelancer.seriesguide.tapeinterval"; public static @StyleRes int THEME = R.style.Theme_SeriesGuide; @Override protected void onCreate(Bundle savedInstanceState) { setTheme(SeriesGuidePreferences.THEME); super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); setupActionBar(); if (savedInstanceState == null) { Fragment f = new SettingsFragment(); FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.add(R.id.containerSettings, f); ft.commit(); // open a sub settings screen if requested String settingsScreen = getIntent().getStringExtra(EXTRA_SETTINGS_SCREEN); if (settingsScreen != null) { switchToSettings(settingsScreen); } } } private void setupActionBar() { Toolbar toolbar = (Toolbar) findViewById(R.id.sgToolbar); setSupportActionBar(toolbar); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); } } @Override public void onBackPressed() { // Because we use the platform fragment manager we need to pop fragments on our own if (!getFragmentManager().popBackStackImmediate()) { super.onBackPressed(); } } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: onBackPressed(); return true; } return super.onOptionsItemSelected(item); } public void switchToSettings(String settingsId) { Bundle args = new Bundle(); args.putString(EXTRA_SETTINGS_SCREEN, settingsId); Fragment f = new SettingsFragment(); f.setArguments(args); FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.replace(R.id.containerSettings, f); ft.addToBackStack(null); ft.commit(); } private static OnPreferenceChangeListener sNoOpChangeListener = new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { Utils.advertiseSubscription(preference.getContext()); // prevent value from getting saved return false; } }; /** * Resets and runs the notification service to take care of potential time shifts when e.g. * changing the time offset. */ private static void resetAndRunNotificationsService(Context context) { NotificationService.resetLastEpisodeAirtime(PreferenceManager .getDefaultSharedPreferences(context)); Utils.runNotificationService(context); } public static void setListPreferenceSummary(@Nullable ListPreference listPref) { if (listPref == null) { return; } // Set summary to be the user-description for the selected value CharSequence entry = listPref.getEntry(); listPref.setSummary(entry == null ? "" : entry.toString().replaceAll("%", "%%")); } public static class SettingsFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener { public static final String KEY_SCREEN_BASIC = "screen_basic"; private static final String KEY_SCREEN_NOTIFICATIONS = "screen_notifications"; private static final String KEY_SCREEN_ADVANCED = "screen_advanced"; private static final int REQUEST_CODE_RINGTONE = 0; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String settings = getArguments() == null ? null : getArguments().getString(EXTRA_SETTINGS_SCREEN); if (settings == null) { addPreferencesFromResource(R.xml.settings_root); setupRootSettings(); } else if (settings.equals(KEY_SCREEN_BASIC)) { addPreferencesFromResource(R.xml.settings_basic); setupBasicSettings(); } else if (settings.equals(KEY_SCREEN_NOTIFICATIONS)) { addPreferencesFromResource(R.xml.settings_notifications); setupNotificationSettings(); } else if (settings.equals(KEY_SCREEN_ADVANCED)) { addPreferencesFromResource(R.xml.settings_advanced); setupAdvancedSettings(); } } private void setupRootSettings() { // display version as About summary findPreference(KEY_ABOUT).setSummary(Utils.getVersionString(getActivity())); } private void updateRootSettings() { // unlock all link Preference unlock = findPreference("com.battlelancer.seriesguide.upgrade"); boolean hasAccessToX = Utils.hasAccessToX(getActivity()); unlock.setSummary(hasAccessToX ? getString(R.string.upgrade_success) : null); // notifications link Preference notifications = findPreference(KEY_SCREEN_NOTIFICATIONS); if (hasAccessToX && NotificationSettings.isNotificationsEnabled(getActivity())) { notifications.setSummary( NotificationSettings.getLatestToIncludeTresholdValue(getActivity())); } else { notifications.setSummary(R.string.pref_notificationssummary); } // SeriesGuide Cloud link Preference cloud = findPreference("com.battlelancer.seriesguide.cloud"); if (hasAccessToX && HexagonSettings.isEnabled(getActivity())) { cloud.setSummary(HexagonSettings.getAccountName(getActivity())); } else { cloud.setSummary(R.string.hexagon_description); } // trakt link Preference trakt = findPreference("com.battlelancer.seriesguide.trakt.connect"); if (TraktCredentials.get(getActivity()).hasCredentials()) { trakt.setSummary(TraktCredentials.get(getActivity()).getUsername()); } else { trakt.setSummary(null); } // Theme switcher Preference themePref = findPreference(DisplaySettings.KEY_THEME); if (hasAccessToX) { themePref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if (DisplaySettings.KEY_THEME.equals(preference.getKey())) { ThemeUtils.updateTheme((String) newValue); Shadows.getInstance().resetShadowColor(); // restart to apply new theme, go back to this settings screen TaskStackBuilder.create(getActivity()) .addNextIntent(new Intent(getActivity(), ShowsActivity.class)) .addNextIntent(getActivity().getIntent()) .startActivities(); } return true; } }); setListPreferenceSummary((ListPreference) themePref); } else { themePref.setOnPreferenceChangeListener(sNoOpChangeListener); themePref.setSummary(R.string.onlyx); } // show currently set values for list prefs setListPreferenceSummary( (ListPreference) findPreference(DisplaySettings.KEY_NUMBERFORMAT)); // set current value of auto-update pref ((SwitchPreference) findPreference(UpdateSettings.KEY_AUTOUPDATE)).setChecked( SgSyncAdapter.isSyncAutomatically(getActivity())); } private void setupNotificationSettings() { Preference enabledPref = findPreference(NotificationSettings.KEY_ENABLED); final Preference thresholdPref = findPreference(NotificationSettings.KEY_THRESHOLD); final Preference favOnlyPref = findPreference(NotificationSettings.KEY_FAVONLY); final Preference vibratePref = findPreference(NotificationSettings.KEY_VIBRATE); final Preference ringtonePref = findPreference(NotificationSettings.KEY_RINGTONE); // allow supporters to enable notifications if (Utils.hasAccessToX(getActivity())) { enabledPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { boolean isChecked = (boolean) newValue; Utils.trackCustomEvent(getActivity(), TAG, "Notifications", isChecked ? "Enable" : "Disable"); thresholdPref.setEnabled(isChecked); favOnlyPref.setEnabled(isChecked); vibratePref.setEnabled(isChecked); ringtonePref.setEnabled(isChecked); Utils.runNotificationService(getActivity()); return true; } }); favOnlyPref.setOnPreferenceClickListener( new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { resetAndRunNotificationsService(getActivity()); return true; } }); // disable advanced notification settings if notifications are disabled boolean isNotificationsEnabled = NotificationSettings.isNotificationsEnabled( getActivity()); thresholdPref.setEnabled(isNotificationsEnabled); favOnlyPref.setEnabled(isNotificationsEnabled); vibratePref.setEnabled(isNotificationsEnabled); ringtonePref.setEnabled(isNotificationsEnabled); } else { enabledPref.setOnPreferenceChangeListener(sNoOpChangeListener); ((SwitchPreference) enabledPref).setChecked(false); enabledPref.setSummary(R.string.onlyx); thresholdPref.setEnabled(false); favOnlyPref.setEnabled(false); vibratePref.setEnabled(false); ringtonePref.setEnabled(false); } setListPreferenceSummary((ListPreference) thresholdPref); } private void setupBasicSettings() { // No aired episodes findPreference(DisplaySettings.KEY_NO_RELEASED_EPISODES).setOnPreferenceClickListener( new OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { boolean isChecked = ((CheckBoxPreference) preference).isChecked(); Utils.trackCustomEvent(getActivity(), TAG, "OnlyFutureEpisodes", isChecked ? "Enable" : "Disable"); return false; } }); // No special episodes findPreference(DisplaySettings.KEY_HIDE_SPECIALS).setOnPreferenceClickListener( new OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { boolean isChecked = ((CheckBoxPreference) preference).isChecked(); Utils.trackCustomEvent(getActivity(), TAG, "OnlySeasonEpisodes", isChecked ? "Enable" : "Disable"); return false; } }); // show currently set values for list prefs setListPreferenceSummary((ListPreference) findPreference(DisplaySettings.KEY_LANGUAGE)); ListPreference offsetListPref = (ListPreference) findPreference(KEY_OFFSET); offsetListPref.setSummary(getString(R.string.pref_offsetsummary, offsetListPref.getEntry())); } private void setupAdvancedSettings() { // Clear image cache findPreference(KEY_CLEAR_CACHE) .setOnPreferenceClickListener(new OnPreferenceClickListener() { public boolean onPreferenceClick(Preference preference) { // try to open app info where user can clear app cache folders Intent intent = new Intent( android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.parse("package:" + getActivity().getPackageName())); if (!Utils.tryStartActivity(getActivity(), intent, false)) { // try to open all apps view if detail view not available intent = new Intent( android.provider.Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS); Utils.tryStartActivity(getActivity(), intent, true); } return true; } }); // GA opt-out findPreference(AppSettings.KEY_GOOGLEANALYTICS).setOnPreferenceChangeListener( new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if (preference.getKey().equals(AppSettings.KEY_GOOGLEANALYTICS)) { boolean isEnabled = (Boolean) newValue; GoogleAnalytics.getInstance(getActivity()).setAppOptOut(isEnabled); return true; } return false; } }); } @Override public void onStart() { super.onStart(); // update some summary values on the root page String settings = getArguments() == null ? null : getArguments().getString(EXTRA_SETTINGS_SCREEN); if (settings == null) { updateRootSettings(); } final SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(getActivity()); prefs.registerOnSharedPreferenceChangeListener(this); } @Override public void onStop() { super.onStop(); final SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(getActivity()); prefs.unregisterOnSharedPreferenceChangeListener(this); } @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, @NonNull Preference preference) { String key = preference.getKey(); if (key != null && key.startsWith("screen_")) { ((SeriesGuidePreferences) getActivity()).switchToSettings(key); return true; } if (NotificationSettings.KEY_RINGTONE.equals(key)) { Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION); // show silent and default options intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true); intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true); intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, Settings.System.DEFAULT_NOTIFICATION_URI); // restore selected sound or silent (null) String existingValue = NotificationSettings.getNotificationsRingtone(getActivity()); intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, TextUtils.isEmpty(existingValue) ? null : Uri.parse(existingValue)); startActivityForResult(intent, REQUEST_CODE_RINGTONE); return true; } if (AdvancedSettings.KEY_AUTOBACKUP.equals(key)) { startActivity(new Intent(getActivity(), DataLiberationActivity.class).putExtra( DataLiberationActivity.InitBundle.EXTRA_SHOW_AUTOBACKUP, true)); return true; } if (KEY_ABOUT.equals(key)) { FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.replace(R.id.containerSettings, new AboutSettingsFragment()); ft.addToBackStack(null); ft.commit(); return true; } return super.onPreferenceTreeClick(preferenceScreen, preference); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (REQUEST_CODE_RINGTONE == requestCode) { if (data != null) { Uri ringtone = data.getParcelableExtra( RingtoneManager.EXTRA_RINGTONE_PICKED_URI); PreferenceManager.getDefaultSharedPreferences(getActivity()).edit() .putString(NotificationSettings.KEY_RINGTONE, ringtone == null ? "" : ringtone.toString()) .apply(); } return; } super.onActivityResult(requestCode, resultCode, data); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { Preference pref = findPreference(key); if (pref != null) { new BackupManager(pref.getContext()).dataChanged(); } // update pref summary text if (DisplaySettings.KEY_LANGUAGE.equals(key) || DisplaySettings.KEY_NUMBERFORMAT.equals(key) || DisplaySettings.KEY_THEME.equals(key) || NotificationSettings.KEY_THRESHOLD.equals(key) ) { if (pref != null) { setListPreferenceSummary((ListPreference) pref); } } if (KEY_OFFSET.equals(key)) { if (pref != null) { ListPreference listPref = (ListPreference) pref; // Set summary to be the user-description for the selected value listPref.setSummary( getString(R.string.pref_offsetsummary, listPref.getEntry())); } } // pref changes that require the notification service to be reset if (KEY_OFFSET.equals(key) || NotificationSettings.KEY_THRESHOLD.equals(key)) { resetAndRunNotificationsService(getActivity()); } // pref changes that require the widgets to be updated if (KEY_OFFSET.equals(key) || DisplaySettings.KEY_HIDE_SPECIALS.equals(key) || DisplaySettings.KEY_DISPLAY_EXACT_DATE.equals(key) || DisplaySettings.KEY_PREVENT_SPOILERS.equals(key)) { // update any widgets ListWidgetProvider.notifyAllAppWidgetsViewDataChanged(getActivity()); } if (DisplaySettings.KEY_LANGUAGE.equals(key)) { // reset last edit date of all episodes so they will get updated new Thread(new Runnable() { public void run() { ContentValues values = new ContentValues(); values.put(Episodes.LAST_EDITED, 0); getActivity().getContentResolver() .update(Episodes.CONTENT_URI, values, null, null); } }).start(); } // Toggle auto-update on SyncAdapter if (UpdateSettings.KEY_AUTOUPDATE.equals(key)) { if (pref != null) { //noinspection ConstantConditions SwitchPreference autoUpdatePref = (SwitchPreference) pref; SgSyncAdapter.setSyncAutomatically(getActivity(), autoUpdatePref.isChecked()); } } } } }