1 /******************************************************************************* 2 * Copyright (C) 2014 Google Inc. 3 * Licensed to The Android Open Source Project. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 *******************************************************************************/ 17 18 package com.android.mail.ui.settings; 19 20 import android.app.AlertDialog; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.DialogInterface.OnClickListener; 24 import android.os.AsyncTask; 25 import android.os.Bundle; 26 import android.preference.CheckBoxPreference; 27 import android.preference.ListPreference; 28 import android.preference.Preference; 29 import android.preference.Preference.OnPreferenceChangeListener; 30 import android.view.Menu; 31 import android.view.MenuInflater; 32 import android.view.MenuItem; 33 import android.widget.Toast; 34 35 import com.android.mail.preferences.MailPrefs; 36 import com.android.mail.preferences.MailPrefs.PreferenceKeys; 37 import com.android.mail.providers.SuggestionsProvider; 38 import com.android.mail.providers.UIProvider.AutoAdvance; 39 import com.android.mail.utils.LogUtils; 40 import com.android.mail.R; 41 import com.google.common.annotations.VisibleForTesting; 42 43 /** 44 * This fragment shows general app preferences. 45 */ 46 public class GeneralPrefsFragment extends MailPreferenceFragment 47 implements OnClickListener, OnPreferenceChangeListener { 48 49 // Keys used to reference pref widgets which don't map directly to preference entries 50 static final String AUTO_ADVANCE_WIDGET = "auto-advance-widget"; 51 52 static final String CALLED_FROM_TEST = "called-from-test"; 53 54 // Category for removal actions 55 protected static final String REMOVAL_ACTIONS_GROUP = "removal-actions-group"; 56 57 protected MailPrefs mMailPrefs; 58 59 private AlertDialog mClearSearchHistoryDialog; 60 61 private ListPreference mAutoAdvance; 62 private static final int[] AUTO_ADVANCE_VALUES = { 63 AutoAdvance.NEWER, 64 AutoAdvance.OLDER, 65 AutoAdvance.LIST 66 }; 67 68 @Override onCreate(Bundle savedInstanceState)69 public void onCreate(Bundle savedInstanceState) { 70 super.onCreate(savedInstanceState); 71 72 setHasOptionsMenu(true); 73 74 mMailPrefs = MailPrefs.get(getActivity()); 75 76 // Set the shared prefs name to use prefs auto-persist behavior by default. 77 // Any pref more complex than the default (say, involving migration), should set 78 // "persistent=false" in the XML and manually handle preference initialization and change. 79 getPreferenceManager() 80 .setSharedPreferencesName(mMailPrefs.getSharedPreferencesName()); 81 82 addPreferencesFromResource(R.xml.general_preferences); 83 84 mAutoAdvance = (ListPreference) findPreference(AUTO_ADVANCE_WIDGET); 85 } 86 87 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)88 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 89 /* 90 * We deliberately do not call super because our menu includes the parent's menu options to 91 * allow custom ordering. 92 */ 93 menu.clear(); 94 inflater.inflate(R.menu.general_prefs_fragment_menu, menu); 95 } 96 97 @Override onOptionsItemSelected(MenuItem item)98 public boolean onOptionsItemSelected(MenuItem item) { 99 final int itemId = item.getItemId(); 100 if (itemId == R.id.clear_search_history_menu_item) { 101 clearSearchHistory(); 102 return true; 103 } else if (itemId == R.id.clear_picture_approvals_menu_item) { 104 clearDisplayImages(); 105 return true; 106 } 107 108 return super.onOptionsItemSelected(item); 109 } 110 111 @Override onPreferenceChange(Preference preference, Object newValue)112 public boolean onPreferenceChange(Preference preference, Object newValue) { 113 if (getActivity() == null) { 114 // Monkeys cause bad things. This callback may be delayed if for some reason the 115 // preference screen was closed really quickly - just bail then. 116 return false; 117 } 118 119 final String key = preference.getKey(); 120 121 if (PreferenceKeys.REMOVAL_ACTION.equals(key)) { 122 final String removalAction = newValue.toString(); 123 mMailPrefs.setRemovalAction(removalAction); 124 } else if (AUTO_ADVANCE_WIDGET.equals(key)) { 125 final int prefsAutoAdvanceMode = 126 AUTO_ADVANCE_VALUES[mAutoAdvance.findIndexOfValue((String) newValue)]; 127 mMailPrefs.setAutoAdvanceMode(prefsAutoAdvanceMode); 128 } else if (!PreferenceKeys.CONVERSATION_LIST_SWIPE.equals(key) && 129 !PreferenceKeys.SHOW_SENDER_IMAGES.equals(key) && 130 !PreferenceKeys.DEFAULT_REPLY_ALL.equals(key) && 131 !PreferenceKeys.CONVERSATION_OVERVIEW_MODE.equals(key) && 132 !PreferenceKeys.CONFIRM_DELETE.equals(key) && 133 !PreferenceKeys.CONFIRM_ARCHIVE.equals(key) && 134 !PreferenceKeys.CONFIRM_SEND.equals(key)) { 135 return false; 136 } 137 138 return true; 139 } 140 clearDisplayImages()141 private void clearDisplayImages() { 142 final ClearPictureApprovalsDialogFragment fragment = 143 ClearPictureApprovalsDialogFragment.newInstance(); 144 fragment.show(getActivity().getFragmentManager(), 145 ClearPictureApprovalsDialogFragment.FRAGMENT_TAG); 146 } 147 clearSearchHistory()148 private void clearSearchHistory() { 149 mClearSearchHistoryDialog = new AlertDialog.Builder(getActivity()) 150 .setMessage(R.string.clear_history_dialog_message) 151 .setTitle(R.string.clear_history_dialog_title) 152 .setIconAttribute(android.R.attr.alertDialogIcon) 153 .setPositiveButton(R.string.clear, this) 154 .setNegativeButton(R.string.cancel, this) 155 .show(); 156 } 157 158 159 @Override onClick(DialogInterface dialog, int which)160 public void onClick(DialogInterface dialog, int which) { 161 if (dialog.equals(mClearSearchHistoryDialog)) { 162 if (which == DialogInterface.BUTTON_POSITIVE) { 163 final Context context = getActivity(); 164 // Clear the history in the background, as it causes a disk 165 // write. 166 new AsyncTask<Void, Void, Void>() { 167 @Override 168 protected Void doInBackground(Void... params) { 169 final SuggestionsProvider suggestions = 170 new SuggestionsProvider(context); 171 suggestions.clearHistory(); 172 suggestions.cleanup(); 173 return null; 174 } 175 }.execute(); 176 Toast.makeText(getActivity(), R.string.search_history_cleared, Toast.LENGTH_SHORT) 177 .show(); 178 } 179 } 180 } 181 182 @Override onStop()183 public void onStop() { 184 super.onStop(); 185 if (mClearSearchHistoryDialog != null && mClearSearchHistoryDialog.isShowing()) { 186 mClearSearchHistoryDialog.dismiss(); 187 } 188 } 189 190 @Override onResume()191 public void onResume() { 192 super.onResume(); 193 194 // Manually initialize the preference views that require massaging. Prefs that require 195 // massaging include: 196 // 1. a prefs UI control that does not map 1:1 to storage 197 // 2. a pref that must obtain its initial value from migrated storage, and for which we 198 // don't want to always persist a migrated value 199 final int autoAdvanceModeIndex = prefValueToWidgetIndex(AUTO_ADVANCE_VALUES, 200 mMailPrefs.getAutoAdvanceMode(), AutoAdvance.DEFAULT); 201 mAutoAdvance.setValueIndex(autoAdvanceModeIndex); 202 203 listenForPreferenceChange( 204 PreferenceKeys.REMOVAL_ACTION, 205 PreferenceKeys.CONVERSATION_LIST_SWIPE, 206 PreferenceKeys.SHOW_SENDER_IMAGES, 207 PreferenceKeys.DEFAULT_REPLY_ALL, 208 PreferenceKeys.CONVERSATION_OVERVIEW_MODE, 209 AUTO_ADVANCE_WIDGET, 210 PreferenceKeys.CONFIRM_DELETE, 211 PreferenceKeys.CONFIRM_ARCHIVE, 212 PreferenceKeys.CONFIRM_SEND 213 ); 214 } 215 supportsArchive()216 protected boolean supportsArchive() { 217 return true; 218 } 219 220 /** 221 * Converts the prefs value into an index useful for configuring the UI widget, falling back to 222 * the default value if the value from the prefs can't be found for some reason. If neither can 223 * be found, it throws an {@link java.lang.IllegalArgumentException} 224 * 225 * @param conversionArray An array of prefs values, in widget order 226 * @param prefValue Value of the preference 227 * @param defaultValue Default value, as a fallback if we can't map the real value 228 * @return Index of the entry (or fallback) in the conversion array 229 */ 230 @VisibleForTesting prefValueToWidgetIndex(int[] conversionArray, int prefValue, int defaultValue)231 static int prefValueToWidgetIndex(int[] conversionArray, int prefValue, int defaultValue) { 232 for (int i = 0; i < conversionArray.length; i++) { 233 if (conversionArray[i] == prefValue) { 234 return i; 235 } 236 } 237 LogUtils.e(LogUtils.TAG, "Can't map preference value " + prefValue); 238 for (int i = 0; i < conversionArray.length; i++) { 239 if (conversionArray[i] == defaultValue) { 240 return i; 241 } 242 } 243 throw new IllegalArgumentException("Can't map default preference value " + prefValue); 244 } 245 listenForPreferenceChange(String... keys)246 private void listenForPreferenceChange(String... keys) { 247 for (String key : keys) { 248 Preference p = findPreference(key); 249 if (p != null) { 250 p.setOnPreferenceChangeListener(this); 251 } 252 } 253 } 254 } 255