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