1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.contacts.common.preference; 18 19 import android.accounts.Account; 20 import android.content.ContentResolver; 21 import android.content.Context; 22 import android.content.SharedPreferences; 23 import android.content.SharedPreferences.Editor; 24 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 25 import android.net.Uri; 26 import android.os.Bundle; 27 import android.os.Handler; 28 import android.preference.PreferenceManager; 29 import android.provider.ContactsContract; 30 import android.provider.Settings; 31 import android.provider.Settings.SettingNotFoundException; 32 import android.text.TextUtils; 33 34 import com.android.contacts.common.R; 35 import com.android.contacts.common.model.account.AccountWithDataSet; 36 import com.android.contacts.common.model.account.GoogleAccountType; 37 import com.android.contacts.common.model.AccountTypeManager; 38 39 import java.util.ArrayList; 40 import java.util.List; 41 42 /** 43 * Manages user preferences for contacts. 44 */ 45 public class ContactsPreferences implements OnSharedPreferenceChangeListener { 46 47 /** 48 * The value for the DISPLAY_ORDER key to show the given name first. 49 */ 50 public static final int DISPLAY_ORDER_PRIMARY = 1; 51 52 /** 53 * The value for the DISPLAY_ORDER key to show the family name first. 54 */ 55 public static final int DISPLAY_ORDER_ALTERNATIVE = 2; 56 57 public static final String DISPLAY_ORDER_KEY = "android.contacts.DISPLAY_ORDER"; 58 59 /** 60 * The value for the SORT_ORDER key corresponding to sort by given name first. 61 */ 62 public static final int SORT_ORDER_PRIMARY = 1; 63 64 public static final String SORT_ORDER_KEY = "android.contacts.SORT_ORDER"; 65 66 /** 67 * The value for the SORT_ORDER key corresponding to sort by family name first. 68 */ 69 public static final int SORT_ORDER_ALTERNATIVE = 2; 70 71 public static final String PREF_DISPLAY_ONLY_PHONES = "only_phones"; 72 73 public static final boolean PREF_DISPLAY_ONLY_PHONES_DEFAULT = false; 74 75 public static final String DO_NOT_SYNC_CONTACT_METADATA_MSG = "Do not sync metadata"; 76 77 public static final String CONTACT_METADATA_AUTHORITY = "com.android.contacts.metadata"; 78 79 public static final String SHOULD_CLEAR_METADATA_BEFORE_SYNCING = 80 "should_clear_metadata_before_syncing"; 81 82 public static final String ONLY_CLEAR_DONOT_SYNC = "only_clear_donot_sync"; 83 /** 84 * Value to use when a preference is unassigned and needs to be read from the shared preferences 85 */ 86 private static final int PREFERENCE_UNASSIGNED = -1; 87 88 private final Context mContext; 89 private int mSortOrder = PREFERENCE_UNASSIGNED; 90 private int mDisplayOrder = PREFERENCE_UNASSIGNED; 91 private String mDefaultAccount = null; 92 private ChangeListener mListener = null; 93 private Handler mHandler; 94 private final SharedPreferences mPreferences; 95 private String mDefaultAccountKey; 96 private String mDefaultAccountSavedKey; 97 ContactsPreferences(Context context)98 public ContactsPreferences(Context context) { 99 mContext = context; 100 mHandler = new Handler(); 101 mPreferences = mContext.getSharedPreferences(context.getPackageName(), 102 Context.MODE_PRIVATE); 103 mDefaultAccountKey = mContext.getResources().getString( 104 R.string.contact_editor_default_account_key); 105 mDefaultAccountSavedKey = mContext.getResources().getString( 106 R.string.contact_editor_anything_saved_key); 107 maybeMigrateSystemSettings(); 108 } 109 isSortOrderUserChangeable()110 public boolean isSortOrderUserChangeable() { 111 return mContext.getResources().getBoolean(R.bool.config_sort_order_user_changeable); 112 } 113 getDefaultSortOrder()114 public int getDefaultSortOrder() { 115 if (mContext.getResources().getBoolean(R.bool.config_default_sort_order_primary)) { 116 return SORT_ORDER_PRIMARY; 117 } else { 118 return SORT_ORDER_ALTERNATIVE; 119 } 120 } 121 getSortOrder()122 public int getSortOrder() { 123 if (!isSortOrderUserChangeable()) { 124 return getDefaultSortOrder(); 125 } 126 if (mSortOrder == PREFERENCE_UNASSIGNED) { 127 mSortOrder = mPreferences.getInt(SORT_ORDER_KEY, getDefaultSortOrder()); 128 } 129 return mSortOrder; 130 } 131 setSortOrder(int sortOrder)132 public void setSortOrder(int sortOrder) { 133 mSortOrder = sortOrder; 134 final Editor editor = mPreferences.edit(); 135 editor.putInt(SORT_ORDER_KEY, sortOrder); 136 editor.commit(); 137 } 138 isDisplayOrderUserChangeable()139 public boolean isDisplayOrderUserChangeable() { 140 return mContext.getResources().getBoolean(R.bool.config_display_order_user_changeable); 141 } 142 getDefaultDisplayOrder()143 public int getDefaultDisplayOrder() { 144 if (mContext.getResources().getBoolean(R.bool.config_default_display_order_primary)) { 145 return DISPLAY_ORDER_PRIMARY; 146 } else { 147 return DISPLAY_ORDER_ALTERNATIVE; 148 } 149 } 150 getDisplayOrder()151 public int getDisplayOrder() { 152 if (!isDisplayOrderUserChangeable()) { 153 return getDefaultDisplayOrder(); 154 } 155 if (mDisplayOrder == PREFERENCE_UNASSIGNED) { 156 mDisplayOrder = mPreferences.getInt(DISPLAY_ORDER_KEY, getDefaultDisplayOrder()); 157 } 158 return mDisplayOrder; 159 } 160 setDisplayOrder(int displayOrder)161 public void setDisplayOrder(int displayOrder) { 162 mDisplayOrder = displayOrder; 163 final Editor editor = mPreferences.edit(); 164 editor.putInt(DISPLAY_ORDER_KEY, displayOrder); 165 editor.commit(); 166 } 167 isDefaultAccountUserChangeable()168 public boolean isDefaultAccountUserChangeable() { 169 return mContext.getResources().getBoolean(R.bool.config_default_account_user_changeable); 170 } 171 getDefaultAccount()172 public String getDefaultAccount() { 173 if (!isDefaultAccountUserChangeable()) { 174 return mDefaultAccount; 175 } 176 if (TextUtils.isEmpty(mDefaultAccount)) { 177 final String accountString = mPreferences 178 .getString(mDefaultAccountKey, mDefaultAccount); 179 if (!TextUtils.isEmpty(accountString)) { 180 final AccountWithDataSet accountWithDataSet = AccountWithDataSet.unstringify( 181 accountString); 182 mDefaultAccount = accountWithDataSet.name; 183 } 184 } 185 return mDefaultAccount; 186 } 187 setDefaultAccount(AccountWithDataSet accountWithDataSet)188 public void setDefaultAccount(AccountWithDataSet accountWithDataSet) { 189 mDefaultAccount = accountWithDataSet == null ? null : accountWithDataSet.name; 190 final Editor editor = mPreferences.edit(); 191 if (TextUtils.isEmpty(mDefaultAccount)) { 192 editor.remove(mDefaultAccountKey); 193 } else { 194 editor.putString(mDefaultAccountKey, accountWithDataSet.stringify()); 195 } 196 editor.putBoolean(mDefaultAccountSavedKey, true); 197 editor.commit(); 198 } 199 getContactMetadataSyncAccountName()200 public String getContactMetadataSyncAccountName() { 201 final Account syncAccount = getContactMetadataSyncAccount(); 202 return syncAccount == null ? DO_NOT_SYNC_CONTACT_METADATA_MSG : syncAccount.name; 203 } 204 setContactMetadataSyncAccount(AccountWithDataSet accountWithDataSet)205 public void setContactMetadataSyncAccount(AccountWithDataSet accountWithDataSet) { 206 final String mContactMetadataSyncAccount = 207 accountWithDataSet == null ? null : accountWithDataSet.name; 208 requestMetadataSyncForAccount(mContactMetadataSyncAccount); 209 } 210 getContactMetadataSyncAccount()211 private Account getContactMetadataSyncAccount() { 212 for (Account account : getFocusGoogleAccounts()) { 213 if (ContentResolver.getIsSyncable(account, CONTACT_METADATA_AUTHORITY) == 1 214 && ContentResolver.getSyncAutomatically(account, CONTACT_METADATA_AUTHORITY)) { 215 return account; 216 } 217 } 218 return null; 219 } 220 221 /** 222 * Turn on contact metadata sync for this {@param accountName} and turn off automatic sync 223 * for other accounts. If accountName is null, then turn off automatic sync for all accounts. 224 */ requestMetadataSyncForAccount(String accountName)225 private void requestMetadataSyncForAccount(String accountName) { 226 for (Account account : getFocusGoogleAccounts()) { 227 if (!TextUtils.isEmpty(accountName) && accountName.equals(account.name)) { 228 // Request sync. 229 final Bundle b = new Bundle(); 230 b.putBoolean(SHOULD_CLEAR_METADATA_BEFORE_SYNCING, true); 231 b.putBoolean(ONLY_CLEAR_DONOT_SYNC, false); 232 b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); 233 b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); 234 ContentResolver.requestSync(account, CONTACT_METADATA_AUTHORITY, b); 235 236 ContentResolver.setSyncAutomatically(account, CONTACT_METADATA_AUTHORITY, true); 237 } else if (ContentResolver.getSyncAutomatically(account, CONTACT_METADATA_AUTHORITY)) { 238 // Turn off automatic sync for previous sync account. 239 ContentResolver.setSyncAutomatically(account, CONTACT_METADATA_AUTHORITY, false); 240 if (TextUtils.isEmpty(accountName)) { 241 // Request sync to clear old data. 242 final Bundle b = new Bundle(); 243 b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); 244 b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); 245 b.putBoolean(SHOULD_CLEAR_METADATA_BEFORE_SYNCING, true); 246 b.putBoolean(ONLY_CLEAR_DONOT_SYNC, true); 247 ContentResolver.requestSync(account, CONTACT_METADATA_AUTHORITY, b); 248 } 249 } 250 } 251 } 252 253 /** 254 * @return google accounts with "com.google" account type and null data set. 255 */ getFocusGoogleAccounts()256 private List<Account> getFocusGoogleAccounts() { 257 List<Account> focusGoogleAccounts = new ArrayList<Account>(); 258 final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(mContext); 259 List<AccountWithDataSet> accounts = accountTypeManager.getAccounts(true); 260 for (AccountWithDataSet account : accounts) { 261 if (GoogleAccountType.ACCOUNT_TYPE.equals(account.type) && account.dataSet == null) { 262 focusGoogleAccounts.add(account.getAccountOrNull()); 263 } 264 } 265 return focusGoogleAccounts; 266 } 267 registerChangeListener(ChangeListener listener)268 public void registerChangeListener(ChangeListener listener) { 269 if (mListener != null) unregisterChangeListener(); 270 271 mListener = listener; 272 273 // Reset preferences to "unknown" because they may have changed while the 274 // listener was unregistered. 275 mDisplayOrder = PREFERENCE_UNASSIGNED; 276 mSortOrder = PREFERENCE_UNASSIGNED; 277 mDefaultAccount = null; 278 279 mPreferences.registerOnSharedPreferenceChangeListener(this); 280 } 281 unregisterChangeListener()282 public void unregisterChangeListener() { 283 if (mListener != null) { 284 mListener = null; 285 } 286 287 mPreferences.unregisterOnSharedPreferenceChangeListener(this); 288 } 289 290 @Override onSharedPreferenceChanged(SharedPreferences sharedPreferences, final String key)291 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, final String key) { 292 // This notification is not sent on the Ui thread. Use the previously created Handler 293 // to switch to the Ui thread 294 mHandler.post(new Runnable() { 295 @Override 296 public void run() { 297 refreshValue(key); 298 } 299 }); 300 } 301 302 /** 303 * Forces the value for the given key to be looked up from shared preferences and notifies 304 * the registered {@link ChangeListener} 305 * 306 * @param key the {@link SharedPreferences} key to look up 307 */ refreshValue(String key)308 public void refreshValue(String key) { 309 if (DISPLAY_ORDER_KEY.equals(key)) { 310 mDisplayOrder = PREFERENCE_UNASSIGNED; 311 mDisplayOrder = getDisplayOrder(); 312 } else if (SORT_ORDER_KEY.equals(key)) { 313 mSortOrder = PREFERENCE_UNASSIGNED; 314 mSortOrder = getSortOrder(); 315 } else if (mDefaultAccountKey.equals(key)) { 316 mDefaultAccount = null; 317 mDefaultAccount = getDefaultAccount(); 318 } 319 if (mListener != null) mListener.onChange(); 320 } 321 322 public interface ChangeListener { onChange()323 void onChange(); 324 } 325 326 /** 327 * If there are currently no preferences (which means this is the first time we are run), 328 * For sort order and display order, check to see if there are any preferences stored in 329 * system settings (pre-L) which can be copied into our own SharedPreferences. 330 * For default account setting, check to see if there are any preferences stored in the previous 331 * SharedPreferences which can be copied into current SharedPreferences. 332 */ maybeMigrateSystemSettings()333 private void maybeMigrateSystemSettings() { 334 if (!mPreferences.contains(SORT_ORDER_KEY)) { 335 int sortOrder = getDefaultSortOrder(); 336 try { 337 sortOrder = Settings.System.getInt(mContext.getContentResolver(), 338 SORT_ORDER_KEY); 339 } catch (SettingNotFoundException e) { 340 } 341 setSortOrder(sortOrder); 342 } 343 344 if (!mPreferences.contains(DISPLAY_ORDER_KEY)) { 345 int displayOrder = getDefaultDisplayOrder(); 346 try { 347 displayOrder = Settings.System.getInt(mContext.getContentResolver(), 348 DISPLAY_ORDER_KEY); 349 } catch (SettingNotFoundException e) { 350 } 351 setDisplayOrder(displayOrder); 352 } 353 354 if (!mPreferences.contains(mDefaultAccountKey)) { 355 final SharedPreferences previousPrefs = 356 PreferenceManager.getDefaultSharedPreferences(mContext); 357 final String defaultAccount = previousPrefs.getString(mDefaultAccountKey, null); 358 if (!TextUtils.isEmpty(defaultAccount)) { 359 final AccountWithDataSet accountWithDataSet = AccountWithDataSet.unstringify( 360 defaultAccount); 361 setDefaultAccount(accountWithDataSet); 362 } 363 } 364 } 365 } 366