blob: ac7b0e02099bb1629655ef1379470896d2bac3a1 [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
17package com.android.contacts.common.preference;
18
Chiao Cheng89437e82012-11-01 13:41:51 -070019import android.content.Context;
Yorke Leeb3d841a2014-07-10 11:38:55 -070020import android.content.SharedPreferences;
21import android.content.SharedPreferences.Editor;
22import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
Chiao Cheng89437e82012-11-01 13:41:51 -070023import android.os.Handler;
Marcus Hagerott949d4e82016-09-20 13:23:05 -070024import android.os.Looper;
Tingting Wang1604c1e2015-10-28 15:51:32 -070025import android.preference.PreferenceManager;
Chiao Cheng89437e82012-11-01 13:41:51 -070026import android.provider.Settings;
27import android.provider.Settings.SettingNotFoundException;
Marcus Hagerott949d4e82016-09-20 13:23:05 -070028import android.support.annotation.NonNull;
29import android.support.annotation.VisibleForTesting;
Tingting Wang20c6ec52015-08-20 16:35:36 -070030import android.text.TextUtils;
Chiao Cheng89437e82012-11-01 13:41:51 -070031
32import com.android.contacts.common.R;
Tingting Wang20c6ec52015-08-20 16:35:36 -070033import com.android.contacts.common.model.account.AccountWithDataSet;
Tingting Wang3571ced2016-01-13 10:29:44 -080034
Tingting Wang3571ced2016-01-13 10:29:44 -080035import java.util.List;
Chiao Cheng89437e82012-11-01 13:41:51 -070036
37/**
38 * Manages user preferences for contacts.
39 */
Brandon Maxwellc0316362016-02-03 16:28:06 -080040public class ContactsPreferences implements OnSharedPreferenceChangeListener {
Yorke Leeb3d841a2014-07-10 11:38:55 -070041
42 /**
43 * The value for the DISPLAY_ORDER key to show the given name first.
44 */
45 public static final int DISPLAY_ORDER_PRIMARY = 1;
46
47 /**
48 * The value for the DISPLAY_ORDER key to show the family name first.
49 */
50 public static final int DISPLAY_ORDER_ALTERNATIVE = 2;
51
52 public static final String DISPLAY_ORDER_KEY = "android.contacts.DISPLAY_ORDER";
53
54 /**
55 * The value for the SORT_ORDER key corresponding to sort by given name first.
56 */
57 public static final int SORT_ORDER_PRIMARY = 1;
58
59 public static final String SORT_ORDER_KEY = "android.contacts.SORT_ORDER";
60
61 /**
62 * The value for the SORT_ORDER key corresponding to sort by family name first.
63 */
64 public static final int SORT_ORDER_ALTERNATIVE = 2;
Chiao Cheng89437e82012-11-01 13:41:51 -070065
66 public static final String PREF_DISPLAY_ONLY_PHONES = "only_phones";
Brandon Maxwelle8862172015-10-22 16:19:46 -070067
Chiao Cheng89437e82012-11-01 13:41:51 -070068 public static final boolean PREF_DISPLAY_ONLY_PHONES_DEFAULT = false;
69
Brandon Maxwelle8862172015-10-22 16:19:46 -070070 /**
71 * Value to use when a preference is unassigned and needs to be read from the shared preferences
72 */
73 private static final int PREFERENCE_UNASSIGNED = -1;
74
Yorke Leeb3d841a2014-07-10 11:38:55 -070075 private final Context mContext;
Brandon Maxwelle8862172015-10-22 16:19:46 -070076 private int mSortOrder = PREFERENCE_UNASSIGNED;
77 private int mDisplayOrder = PREFERENCE_UNASSIGNED;
Marcus Hagerottfac695a2016-08-24 17:02:40 -070078 private AccountWithDataSet mDefaultAccount = null;
Chiao Cheng89437e82012-11-01 13:41:51 -070079 private ChangeListener mListener = null;
80 private Handler mHandler;
Yorke Leeb3d841a2014-07-10 11:38:55 -070081 private final SharedPreferences mPreferences;
Marcus Hagerott949d4e82016-09-20 13:23:05 -070082 private final boolean mIsDefaultAccountUserChangeable;
Tingting Wang20c6ec52015-08-20 16:35:36 -070083 private String mDefaultAccountKey;
Chiao Cheng89437e82012-11-01 13:41:51 -070084
85 public ContactsPreferences(Context context) {
Marcus Hagerott949d4e82016-09-20 13:23:05 -070086 this(context,
87 context.getResources().getBoolean(R.bool.config_default_account_user_changeable));
88 }
89
90 @VisibleForTesting
91 ContactsPreferences(Context context, boolean isDefaultAccountUserChangeable) {
Chiao Cheng89437e82012-11-01 13:41:51 -070092 mContext = context;
Marcus Hagerott949d4e82016-09-20 13:23:05 -070093 mIsDefaultAccountUserChangeable = isDefaultAccountUserChangeable;
94
95 mHandler = new Handler(Looper.getMainLooper());
Yorke Leeb3d841a2014-07-10 11:38:55 -070096 mPreferences = mContext.getSharedPreferences(context.getPackageName(),
97 Context.MODE_PRIVATE);
Tingting Wang20c6ec52015-08-20 16:35:36 -070098 mDefaultAccountKey = mContext.getResources().getString(
99 R.string.contact_editor_default_account_key);
Yorke Leeb3d841a2014-07-10 11:38:55 -0700100 maybeMigrateSystemSettings();
Chiao Cheng89437e82012-11-01 13:41:51 -0700101 }
102
103 public boolean isSortOrderUserChangeable() {
104 return mContext.getResources().getBoolean(R.bool.config_sort_order_user_changeable);
105 }
106
107 public int getDefaultSortOrder() {
108 if (mContext.getResources().getBoolean(R.bool.config_default_sort_order_primary)) {
Yorke Leeb3d841a2014-07-10 11:38:55 -0700109 return SORT_ORDER_PRIMARY;
Chiao Cheng89437e82012-11-01 13:41:51 -0700110 } else {
Yorke Leeb3d841a2014-07-10 11:38:55 -0700111 return SORT_ORDER_ALTERNATIVE;
Chiao Cheng89437e82012-11-01 13:41:51 -0700112 }
113 }
114
115 public int getSortOrder() {
116 if (!isSortOrderUserChangeable()) {
117 return getDefaultSortOrder();
118 }
Brandon Maxwelle8862172015-10-22 16:19:46 -0700119 if (mSortOrder == PREFERENCE_UNASSIGNED) {
Yorke Leeb3d841a2014-07-10 11:38:55 -0700120 mSortOrder = mPreferences.getInt(SORT_ORDER_KEY, getDefaultSortOrder());
Chiao Cheng89437e82012-11-01 13:41:51 -0700121 }
122 return mSortOrder;
123 }
124
125 public void setSortOrder(int sortOrder) {
126 mSortOrder = sortOrder;
Yorke Leeb3d841a2014-07-10 11:38:55 -0700127 final Editor editor = mPreferences.edit();
128 editor.putInt(SORT_ORDER_KEY, sortOrder);
129 editor.commit();
Chiao Cheng89437e82012-11-01 13:41:51 -0700130 }
131
132 public boolean isDisplayOrderUserChangeable() {
133 return mContext.getResources().getBoolean(R.bool.config_display_order_user_changeable);
134 }
135
136 public int getDefaultDisplayOrder() {
137 if (mContext.getResources().getBoolean(R.bool.config_default_display_order_primary)) {
Yorke Leeb3d841a2014-07-10 11:38:55 -0700138 return DISPLAY_ORDER_PRIMARY;
Chiao Cheng89437e82012-11-01 13:41:51 -0700139 } else {
Yorke Leeb3d841a2014-07-10 11:38:55 -0700140 return DISPLAY_ORDER_ALTERNATIVE;
Chiao Cheng89437e82012-11-01 13:41:51 -0700141 }
142 }
143
144 public int getDisplayOrder() {
145 if (!isDisplayOrderUserChangeable()) {
146 return getDefaultDisplayOrder();
147 }
Brandon Maxwelle8862172015-10-22 16:19:46 -0700148 if (mDisplayOrder == PREFERENCE_UNASSIGNED) {
Yorke Leeb3d841a2014-07-10 11:38:55 -0700149 mDisplayOrder = mPreferences.getInt(DISPLAY_ORDER_KEY, getDefaultDisplayOrder());
Chiao Cheng89437e82012-11-01 13:41:51 -0700150 }
151 return mDisplayOrder;
152 }
153
154 public void setDisplayOrder(int displayOrder) {
155 mDisplayOrder = displayOrder;
Yorke Leeb3d841a2014-07-10 11:38:55 -0700156 final Editor editor = mPreferences.edit();
157 editor.putInt(DISPLAY_ORDER_KEY, displayOrder);
158 editor.commit();
Chiao Cheng89437e82012-11-01 13:41:51 -0700159 }
160
Tingting Wang20c6ec52015-08-20 16:35:36 -0700161 public boolean isDefaultAccountUserChangeable() {
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700162 return mIsDefaultAccountUserChangeable;
Tingting Wang20c6ec52015-08-20 16:35:36 -0700163 }
164
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700165 public AccountWithDataSet getDefaultAccount() {
Tingting Wang20c6ec52015-08-20 16:35:36 -0700166 if (!isDefaultAccountUserChangeable()) {
167 return mDefaultAccount;
168 }
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700169 if (mDefaultAccount == null) {
Brandon Maxwelle8862172015-10-22 16:19:46 -0700170 final String accountString = mPreferences
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700171 .getString(mDefaultAccountKey, null);
Tingting Wang20c6ec52015-08-20 16:35:36 -0700172 if (!TextUtils.isEmpty(accountString)) {
Marcus Hagerottfac695a2016-08-24 17:02:40 -0700173 mDefaultAccount = AccountWithDataSet.unstringify(accountString);
Tingting Wang20c6ec52015-08-20 16:35:36 -0700174 }
175 }
176 return mDefaultAccount;
177 }
178
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700179 public void clearDefaultAccount() {
180 mDefaultAccount = null;
181 mPreferences.edit().remove(mDefaultAccountKey).commit();
182 }
183
184 public void setDefaultAccount(@NonNull AccountWithDataSet accountWithDataSet) {
185 if (accountWithDataSet == null) {
186 throw new IllegalArgumentException(
187 "argument should not be null");
Tingting Wang20c6ec52015-08-20 16:35:36 -0700188 }
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700189 mDefaultAccount = accountWithDataSet;
190 mPreferences.edit().putString(mDefaultAccountKey, accountWithDataSet.stringify()).commit();
191 }
192
Marcus Hagerott819214d2016-09-29 14:58:27 -0700193 public boolean isDefaultAccountSet() {
194 return mDefaultAccount != null || mPreferences.contains(mDefaultAccountKey);
195 }
196
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700197 /**
198 * @return false if there is only one writable account or no requirement to return true is met.
199 * true if the contact editor should show the "accounts changed" notification, that is:
200 * - If it's the first launch.
201 * - Or, if the default account has been removed.
202 * (And some extra sanity check)
203 *
204 * Note if this method returns {@code false}, the caller can safely assume that
205 * {@link #getDefaultAccount} will return a valid account. (Either an account which still
206 * exists, or {@code null} which should be interpreted as "local only".)
207 */
208 public boolean shouldShowAccountChangedNotification(List<AccountWithDataSet>
209 currentWritableAccounts) {
210 final AccountWithDataSet defaultAccount = getDefaultAccount();
211
212 // This shouldn't occur anymore because a "device" account is added in the case that there
213 // are no other accounts but if there are no writable accounts then the default has been
214 // initialized if it is "device"
215 if (currentWritableAccounts.isEmpty()) {
216 return defaultAccount == null || !defaultAccount.isNullAccount();
217 }
218
Gary Mai3107b252016-11-02 18:26:07 -0700219 if (currentWritableAccounts.size() == 1
220 && !currentWritableAccounts.get(0).isNullAccount()) {
Marcus Hagerott949d4e82016-09-20 13:23:05 -0700221 return false;
222 }
223
224 if (defaultAccount == null) {
225 return true;
226 }
227
228 if (!currentWritableAccounts.contains(defaultAccount)) {
229 return true;
230 }
231
232 // All good.
233 return false;
Tingting Wang20c6ec52015-08-20 16:35:36 -0700234 }
235
Chiao Cheng89437e82012-11-01 13:41:51 -0700236 public void registerChangeListener(ChangeListener listener) {
237 if (mListener != null) unregisterChangeListener();
238
239 mListener = listener;
240
241 // Reset preferences to "unknown" because they may have changed while the
Yorke Leeb3d841a2014-07-10 11:38:55 -0700242 // listener was unregistered.
Brandon Maxwelle8862172015-10-22 16:19:46 -0700243 mDisplayOrder = PREFERENCE_UNASSIGNED;
244 mSortOrder = PREFERENCE_UNASSIGNED;
Tingting Wangc939ae32015-09-01 16:45:08 -0700245 mDefaultAccount = null;
Chiao Cheng89437e82012-11-01 13:41:51 -0700246
Yorke Leeb3d841a2014-07-10 11:38:55 -0700247 mPreferences.registerOnSharedPreferenceChangeListener(this);
Chiao Cheng89437e82012-11-01 13:41:51 -0700248 }
249
250 public void unregisterChangeListener() {
251 if (mListener != null) {
Chiao Cheng89437e82012-11-01 13:41:51 -0700252 mListener = null;
253 }
Yorke Leeb3d841a2014-07-10 11:38:55 -0700254
255 mPreferences.unregisterOnSharedPreferenceChangeListener(this);
Chiao Cheng89437e82012-11-01 13:41:51 -0700256 }
257
258 @Override
Yorke Leeb3d841a2014-07-10 11:38:55 -0700259 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, final String key) {
Chiao Cheng89437e82012-11-01 13:41:51 -0700260 // This notification is not sent on the Ui thread. Use the previously created Handler
261 // to switch to the Ui thread
262 mHandler.post(new Runnable() {
263 @Override
264 public void run() {
Brandon Maxwelle8862172015-10-22 16:19:46 -0700265 refreshValue(key);
Chiao Cheng89437e82012-11-01 13:41:51 -0700266 }
267 });
268 }
269
Brandon Maxwelle8862172015-10-22 16:19:46 -0700270 /**
271 * Forces the value for the given key to be looked up from shared preferences and notifies
272 * the registered {@link ChangeListener}
273 *
274 * @param key the {@link SharedPreferences} key to look up
275 */
276 public void refreshValue(String key) {
277 if (DISPLAY_ORDER_KEY.equals(key)) {
278 mDisplayOrder = PREFERENCE_UNASSIGNED;
279 mDisplayOrder = getDisplayOrder();
280 } else if (SORT_ORDER_KEY.equals(key)) {
281 mSortOrder = PREFERENCE_UNASSIGNED;
282 mSortOrder = getSortOrder();
283 } else if (mDefaultAccountKey.equals(key)) {
284 mDefaultAccount = null;
285 mDefaultAccount = getDefaultAccount();
286 }
287 if (mListener != null) mListener.onChange();
288 }
289
Chiao Cheng89437e82012-11-01 13:41:51 -0700290 public interface ChangeListener {
291 void onChange();
292 }
Yorke Leeb3d841a2014-07-10 11:38:55 -0700293
294 /**
295 * If there are currently no preferences (which means this is the first time we are run),
Tingting Wang1604c1e2015-10-28 15:51:32 -0700296 * For sort order and display order, check to see if there are any preferences stored in
297 * system settings (pre-L) which can be copied into our own SharedPreferences.
298 * For default account setting, check to see if there are any preferences stored in the previous
299 * SharedPreferences which can be copied into current SharedPreferences.
Yorke Leeb3d841a2014-07-10 11:38:55 -0700300 */
301 private void maybeMigrateSystemSettings() {
302 if (!mPreferences.contains(SORT_ORDER_KEY)) {
303 int sortOrder = getDefaultSortOrder();
304 try {
305 sortOrder = Settings.System.getInt(mContext.getContentResolver(),
306 SORT_ORDER_KEY);
307 } catch (SettingNotFoundException e) {
308 }
309 setSortOrder(sortOrder);
310 }
311
312 if (!mPreferences.contains(DISPLAY_ORDER_KEY)) {
313 int displayOrder = getDefaultDisplayOrder();
314 try {
315 displayOrder = Settings.System.getInt(mContext.getContentResolver(),
316 DISPLAY_ORDER_KEY);
317 } catch (SettingNotFoundException e) {
318 }
319 setDisplayOrder(displayOrder);
320 }
Tingting Wang1604c1e2015-10-28 15:51:32 -0700321
322 if (!mPreferences.contains(mDefaultAccountKey)) {
323 final SharedPreferences previousPrefs =
324 PreferenceManager.getDefaultSharedPreferences(mContext);
325 final String defaultAccount = previousPrefs.getString(mDefaultAccountKey, null);
326 if (!TextUtils.isEmpty(defaultAccount)) {
327 final AccountWithDataSet accountWithDataSet = AccountWithDataSet.unstringify(
328 defaultAccount);
329 setDefaultAccount(accountWithDataSet);
330 }
331 }
Yorke Leeb3d841a2014-07-10 11:38:55 -0700332 }
Marcus Hagerott073a0912016-09-28 17:05:54 -0700333
Chiao Cheng89437e82012-11-01 13:41:51 -0700334}