blob: 48fb205209327ff068aca4df5854b84591dc7eb4 [file] [log] [blame]
Chiao Cheng89437e82012-11-01 13:41:51 -07001/*
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
Gary Mai69c182a2016-12-05 13:07:03 -080017package com.android.contacts.preference;
Chiao Cheng89437e82012-11-01 13:41:51 -070018
James Laskeybc0137f2016-12-06 09:05:51 -080019import android.app.backup.BackupAgent;
20import android.app.backup.BackupManager;
Chiao Cheng89437e82012-11-01 13:41:51 -070021import android.content.Context;
Yorke Leeb3d841a2014-07-10 11:38:55 -070022import android.content.SharedPreferences;
23import android.content.SharedPreferences.Editor;
24import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
Chiao Cheng89437e82012-11-01 13:41:51 -070025import android.os.Handler;
Marcus Hagerott949d4e82016-09-20 13:23:05 -070026import android.os.Looper;
Tingting Wang1604c1e2015-10-28 15:51:32 -070027import android.preference.PreferenceManager;
Chiao Cheng89437e82012-11-01 13:41:51 -070028import android.provider.Settings;
29import android.provider.Settings.SettingNotFoundException;
Marcus Hagerott949d4e82016-09-20 13:23:05 -070030import android.support.annotation.NonNull;
31import android.support.annotation.VisibleForTesting;
Tingting Wang20c6ec52015-08-20 16:35:36 -070032import android.text.TextUtils;
Chiao Cheng89437e82012-11-01 13:41:51 -070033
Arthur Wang3f6a2442016-12-05 14:51:59 -080034import com.android.contacts.R;
Gary Mai69c182a2016-12-05 13:07:03 -080035import com.android.contacts.model.account.AccountWithDataSet;
Tingting Wang3571ced2016-01-13 10:29:44 -080036
Tingting Wang3571ced2016-01-13 10:29:44 -080037import java.util.List;
Chiao Cheng89437e82012-11-01 13:41:51 -070038
39/**
40 * Manages user preferences for contacts.
41 */
Brandon Maxwellc0316362016-02-03 16:28:06 -080042public class ContactsPreferences implements OnSharedPreferenceChangeListener {
Yorke Leeb3d841a2014-07-10 11:38:55 -070043
44 /**
45 * The value for the DISPLAY_ORDER key to show the given name first.
46 */
47 public static final int DISPLAY_ORDER_PRIMARY = 1;
48
49 /**
50 * The value for the DISPLAY_ORDER key to show the family name first.
51 */
52 public static final int DISPLAY_ORDER_ALTERNATIVE = 2;
53
54 public static final String DISPLAY_ORDER_KEY = "android.contacts.DISPLAY_ORDER";
55
56 /**
57 * The value for the SORT_ORDER key corresponding to sort by given name first.
58 */
59 public static final int SORT_ORDER_PRIMARY = 1;
60
61 public static final String SORT_ORDER_KEY = "android.contacts.SORT_ORDER";
62
63 /**
64 * The value for the SORT_ORDER key corresponding to sort by family name first.
65 */
66 public static final int SORT_ORDER_ALTERNATIVE = 2;
Chiao Cheng89437e82012-11-01 13:41:51 -070067
68 public static final String PREF_DISPLAY_ONLY_PHONES = "only_phones";
Brandon Maxwelle8862172015-10-22 16:19:46 -070069
Chiao Cheng89437e82012-11-01 13:41:51 -070070 public static final boolean PREF_DISPLAY_ONLY_PHONES_DEFAULT = false;
71
Brandon Maxwelle8862172015-10-22 16:19:46 -070072 /**
73 * Value to use when a preference is unassigned and needs to be read from the shared preferences
74 */
75 private static final int PREFERENCE_UNASSIGNED = -1;
76
Yorke Leeb3d841a2014-07-10 11:38:55 -070077 private final Context mContext;
Brandon Maxwelle8862172015-10-22 16:19:46 -070078 private int mSortOrder = PREFERENCE_UNASSIGNED;
79 private int mDisplayOrder = PREFERENCE_UNASSIGNED;
Marcus Hagerottfac695a2016-08-24 17:02:40 -070080 private AccountWithDataSet mDefaultAccount = null;
Chiao Cheng89437e82012-11-01 13:41:51 -070081 private ChangeListener mListener = null;
82 private Handler mHandler;
Yorke Leeb3d841a2014-07-10 11:38:55 -070083 private final SharedPreferences mPreferences;
James Laskeybc0137f2016-12-06 09:05:51 -080084 private final BackupManager mBackupManager;
Marcus Hagerott949d4e82016-09-20 13:23:05 -070085 private final boolean mIsDefaultAccountUserChangeable;
Tingting Wang20c6ec52015-08-20 16:35:36 -070086 private String mDefaultAccountKey;
Chiao Cheng89437e82012-11-01 13:41:51 -070087
88 public ContactsPreferences(Context context) {
Marcus Hagerott949d4e82016-09-20 13:23:05 -070089 this(context,
90 context.getResources().getBoolean(R.bool.config_default_account_user_changeable));
91 }
92
93 @VisibleForTesting
94 ContactsPreferences(Context context, boolean isDefaultAccountUserChangeable) {
Chiao Cheng89437e82012-11-01 13:41:51 -070095 mContext = context;
Marcus Hagerott949d4e82016-09-20 13:23:05 -070096 mIsDefaultAccountUserChangeable = isDefaultAccountUserChangeable;
97
James Laskeybc0137f2016-12-06 09:05:51 -080098 mBackupManager = new BackupManager(mContext);
99
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700100 mHandler = new Handler(Looper.getMainLooper());
Yorke Leeb3d841a2014-07-10 11:38:55 -0700101 mPreferences = mContext.getSharedPreferences(context.getPackageName(),
102 Context.MODE_PRIVATE);
Tingting Wang20c6ec52015-08-20 16:35:36 -0700103 mDefaultAccountKey = mContext.getResources().getString(
104 R.string.contact_editor_default_account_key);
Yorke Leeb3d841a2014-07-10 11:38:55 -0700105 maybeMigrateSystemSettings();
Chiao Cheng89437e82012-11-01 13:41:51 -0700106 }
107
108 public boolean isSortOrderUserChangeable() {
109 return mContext.getResources().getBoolean(R.bool.config_sort_order_user_changeable);
110 }
111
112 public int getDefaultSortOrder() {
113 if (mContext.getResources().getBoolean(R.bool.config_default_sort_order_primary)) {
Yorke Leeb3d841a2014-07-10 11:38:55 -0700114 return SORT_ORDER_PRIMARY;
Chiao Cheng89437e82012-11-01 13:41:51 -0700115 } else {
Yorke Leeb3d841a2014-07-10 11:38:55 -0700116 return SORT_ORDER_ALTERNATIVE;
Chiao Cheng89437e82012-11-01 13:41:51 -0700117 }
118 }
119
120 public int getSortOrder() {
121 if (!isSortOrderUserChangeable()) {
122 return getDefaultSortOrder();
123 }
Brandon Maxwelle8862172015-10-22 16:19:46 -0700124 if (mSortOrder == PREFERENCE_UNASSIGNED) {
Yorke Leeb3d841a2014-07-10 11:38:55 -0700125 mSortOrder = mPreferences.getInt(SORT_ORDER_KEY, getDefaultSortOrder());
Chiao Cheng89437e82012-11-01 13:41:51 -0700126 }
127 return mSortOrder;
128 }
129
130 public void setSortOrder(int sortOrder) {
131 mSortOrder = sortOrder;
Yorke Leeb3d841a2014-07-10 11:38:55 -0700132 final Editor editor = mPreferences.edit();
133 editor.putInt(SORT_ORDER_KEY, sortOrder);
134 editor.commit();
James Laskeybc0137f2016-12-06 09:05:51 -0800135 mBackupManager.dataChanged();
Chiao Cheng89437e82012-11-01 13:41:51 -0700136 }
137
138 public boolean isDisplayOrderUserChangeable() {
139 return mContext.getResources().getBoolean(R.bool.config_display_order_user_changeable);
140 }
141
142 public int getDefaultDisplayOrder() {
143 if (mContext.getResources().getBoolean(R.bool.config_default_display_order_primary)) {
Yorke Leeb3d841a2014-07-10 11:38:55 -0700144 return DISPLAY_ORDER_PRIMARY;
Chiao Cheng89437e82012-11-01 13:41:51 -0700145 } else {
Yorke Leeb3d841a2014-07-10 11:38:55 -0700146 return DISPLAY_ORDER_ALTERNATIVE;
Chiao Cheng89437e82012-11-01 13:41:51 -0700147 }
148 }
149
150 public int getDisplayOrder() {
151 if (!isDisplayOrderUserChangeable()) {
152 return getDefaultDisplayOrder();
153 }
Brandon Maxwelle8862172015-10-22 16:19:46 -0700154 if (mDisplayOrder == PREFERENCE_UNASSIGNED) {
Yorke Leeb3d841a2014-07-10 11:38:55 -0700155 mDisplayOrder = mPreferences.getInt(DISPLAY_ORDER_KEY, getDefaultDisplayOrder());
Chiao Cheng89437e82012-11-01 13:41:51 -0700156 }
157 return mDisplayOrder;
158 }
159
160 public void setDisplayOrder(int displayOrder) {
161 mDisplayOrder = displayOrder;
Yorke Leeb3d841a2014-07-10 11:38:55 -0700162 final Editor editor = mPreferences.edit();
163 editor.putInt(DISPLAY_ORDER_KEY, displayOrder);
164 editor.commit();
James Laskeybc0137f2016-12-06 09:05:51 -0800165 mBackupManager.dataChanged();
Chiao Cheng89437e82012-11-01 13:41:51 -0700166 }
167
Tingting Wang20c6ec52015-08-20 16:35:36 -0700168 public boolean isDefaultAccountUserChangeable() {
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700169 return mIsDefaultAccountUserChangeable;
Tingting Wang20c6ec52015-08-20 16:35:36 -0700170 }
171
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700172 public AccountWithDataSet getDefaultAccount() {
Tingting Wang20c6ec52015-08-20 16:35:36 -0700173 if (!isDefaultAccountUserChangeable()) {
174 return mDefaultAccount;
175 }
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700176 if (mDefaultAccount == null) {
Brandon Maxwelle8862172015-10-22 16:19:46 -0700177 final String accountString = mPreferences
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700178 .getString(mDefaultAccountKey, null);
Tingting Wang20c6ec52015-08-20 16:35:36 -0700179 if (!TextUtils.isEmpty(accountString)) {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700180 mDefaultAccount = AccountWithDataSet.unstringify(accountString);
Tingting Wang20c6ec52015-08-20 16:35:36 -0700181 }
182 }
183 return mDefaultAccount;
184 }
185
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700186 public void clearDefaultAccount() {
187 mDefaultAccount = null;
188 mPreferences.edit().remove(mDefaultAccountKey).commit();
189 }
190
191 public void setDefaultAccount(@NonNull AccountWithDataSet accountWithDataSet) {
192 if (accountWithDataSet == null) {
193 throw new IllegalArgumentException(
194 "argument should not be null");
Tingting Wang20c6ec52015-08-20 16:35:36 -0700195 }
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700196 mDefaultAccount = accountWithDataSet;
197 mPreferences.edit().putString(mDefaultAccountKey, accountWithDataSet.stringify()).commit();
198 }
199
Marcus Hagerott819214d2016-09-29 14:58:27 -0700200 public boolean isDefaultAccountSet() {
201 return mDefaultAccount != null || mPreferences.contains(mDefaultAccountKey);
202 }
203
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700204 /**
205 * @return false if there is only one writable account or no requirement to return true is met.
206 * true if the contact editor should show the "accounts changed" notification, that is:
207 * - If it's the first launch.
208 * - Or, if the default account has been removed.
209 * (And some extra sanity check)
210 *
211 * Note if this method returns {@code false}, the caller can safely assume that
212 * {@link #getDefaultAccount} will return a valid account. (Either an account which still
213 * exists, or {@code null} which should be interpreted as "local only".)
214 */
215 public boolean shouldShowAccountChangedNotification(List<AccountWithDataSet>
216 currentWritableAccounts) {
217 final AccountWithDataSet defaultAccount = getDefaultAccount();
218
219 // This shouldn't occur anymore because a "device" account is added in the case that there
220 // are no other accounts but if there are no writable accounts then the default has been
221 // initialized if it is "device"
222 if (currentWritableAccounts.isEmpty()) {
223 return defaultAccount == null || !defaultAccount.isNullAccount();
224 }
225
Gary Mai3107b252016-11-02 18:26:07 -0700226 if (currentWritableAccounts.size() == 1
227 && !currentWritableAccounts.get(0).isNullAccount()) {
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700228 return false;
229 }
230
231 if (defaultAccount == null) {
232 return true;
233 }
234
235 if (!currentWritableAccounts.contains(defaultAccount)) {
236 return true;
237 }
238
239 // All good.
240 return false;
Tingting Wang20c6ec52015-08-20 16:35:36 -0700241 }
242
Chiao Cheng89437e82012-11-01 13:41:51 -0700243 public void registerChangeListener(ChangeListener listener) {
244 if (mListener != null) unregisterChangeListener();
245
246 mListener = listener;
247
248 // Reset preferences to "unknown" because they may have changed while the
Yorke Leeb3d841a2014-07-10 11:38:55 -0700249 // listener was unregistered.
Brandon Maxwelle8862172015-10-22 16:19:46 -0700250 mDisplayOrder = PREFERENCE_UNASSIGNED;
251 mSortOrder = PREFERENCE_UNASSIGNED;
Tingting Wangc939ae32015-09-01 16:45:08 -0700252 mDefaultAccount = null;
Chiao Cheng89437e82012-11-01 13:41:51 -0700253
Yorke Leeb3d841a2014-07-10 11:38:55 -0700254 mPreferences.registerOnSharedPreferenceChangeListener(this);
Chiao Cheng89437e82012-11-01 13:41:51 -0700255 }
256
257 public void unregisterChangeListener() {
258 if (mListener != null) {
Chiao Cheng89437e82012-11-01 13:41:51 -0700259 mListener = null;
260 }
Yorke Leeb3d841a2014-07-10 11:38:55 -0700261
262 mPreferences.unregisterOnSharedPreferenceChangeListener(this);
Chiao Cheng89437e82012-11-01 13:41:51 -0700263 }
264
265 @Override
Yorke Leeb3d841a2014-07-10 11:38:55 -0700266 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, final String key) {
Chiao Cheng89437e82012-11-01 13:41:51 -0700267 // This notification is not sent on the Ui thread. Use the previously created Handler
268 // to switch to the Ui thread
269 mHandler.post(new Runnable() {
270 @Override
271 public void run() {
Brandon Maxwelle8862172015-10-22 16:19:46 -0700272 refreshValue(key);
Chiao Cheng89437e82012-11-01 13:41:51 -0700273 }
274 });
275 }
276
Brandon Maxwelle8862172015-10-22 16:19:46 -0700277 /**
278 * Forces the value for the given key to be looked up from shared preferences and notifies
279 * the registered {@link ChangeListener}
280 *
281 * @param key the {@link SharedPreferences} key to look up
282 */
283 public void refreshValue(String key) {
284 if (DISPLAY_ORDER_KEY.equals(key)) {
285 mDisplayOrder = PREFERENCE_UNASSIGNED;
286 mDisplayOrder = getDisplayOrder();
287 } else if (SORT_ORDER_KEY.equals(key)) {
288 mSortOrder = PREFERENCE_UNASSIGNED;
289 mSortOrder = getSortOrder();
290 } else if (mDefaultAccountKey.equals(key)) {
291 mDefaultAccount = null;
292 mDefaultAccount = getDefaultAccount();
293 }
294 if (mListener != null) mListener.onChange();
295 }
296
Chiao Cheng89437e82012-11-01 13:41:51 -0700297 public interface ChangeListener {
298 void onChange();
299 }
Yorke Leeb3d841a2014-07-10 11:38:55 -0700300
301 /**
302 * If there are currently no preferences (which means this is the first time we are run),
Tingting Wang1604c1e2015-10-28 15:51:32 -0700303 * For sort order and display order, check to see if there are any preferences stored in
304 * system settings (pre-L) which can be copied into our own SharedPreferences.
305 * For default account setting, check to see if there are any preferences stored in the previous
306 * SharedPreferences which can be copied into current SharedPreferences.
Yorke Leeb3d841a2014-07-10 11:38:55 -0700307 */
308 private void maybeMigrateSystemSettings() {
309 if (!mPreferences.contains(SORT_ORDER_KEY)) {
310 int sortOrder = getDefaultSortOrder();
311 try {
312 sortOrder = Settings.System.getInt(mContext.getContentResolver(),
313 SORT_ORDER_KEY);
314 } catch (SettingNotFoundException e) {
315 }
316 setSortOrder(sortOrder);
317 }
318
319 if (!mPreferences.contains(DISPLAY_ORDER_KEY)) {
320 int displayOrder = getDefaultDisplayOrder();
321 try {
322 displayOrder = Settings.System.getInt(mContext.getContentResolver(),
323 DISPLAY_ORDER_KEY);
324 } catch (SettingNotFoundException e) {
325 }
326 setDisplayOrder(displayOrder);
327 }
Tingting Wang1604c1e2015-10-28 15:51:32 -0700328
329 if (!mPreferences.contains(mDefaultAccountKey)) {
330 final SharedPreferences previousPrefs =
331 PreferenceManager.getDefaultSharedPreferences(mContext);
332 final String defaultAccount = previousPrefs.getString(mDefaultAccountKey, null);
333 if (!TextUtils.isEmpty(defaultAccount)) {
334 final AccountWithDataSet accountWithDataSet = AccountWithDataSet.unstringify(
335 defaultAccount);
336 setDefaultAccount(accountWithDataSet);
337 }
338 }
Yorke Leeb3d841a2014-07-10 11:38:55 -0700339 }
Marcus Hagerott073a0912016-09-28 17:05:54 -0700340
Chiao Cheng89437e82012-11-01 13:41:51 -0700341}