Merge "Add a flag for the restore assistant" into ub-contactsdialer-h-dev
diff --git a/res/layout/contact_list_content.xml b/res/layout/contact_list_content.xml
index 8420b26..39f669f 100644
--- a/res/layout/contact_list_content.xml
+++ b/res/layout/contact_list_content.xml
@@ -28,6 +28,37 @@
android:visibility="gone"
android:background="?attr/contact_browser_background" >
+ <LinearLayout
+ android:id="@+id/alert_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="@color/alert_background"
+ android:paddingStart="20dp"
+ android:visibility="gone">
+
+ <TextView
+ android:id="@+id/alert_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:layout_weight="1"
+ android:textColor="@android:color/black"
+ android:textSize="16sp"/>
+
+ <ImageView
+ android:id="@+id/alert_dismiss_icon"
+ android:layout_width="56dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:contentDescription="@string/dismiss_sync_alert"
+ android:background="?android:attr/selectableItemBackground"
+ android:scaleType="center"
+ android:src="@drawable/ic_cancel_black_24dp" />
+ </LinearLayout>
+
<!-- Shown only when an Account filter is set.
- paddingTop should be here to show "shade" effect correctly. -->
<include layout="@layout/account_filter_header" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 98c522d..e2024fd 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -260,4 +260,7 @@
<!-- tint color for device account icons -->
<color name="device_account_tint_color">#7f7f7f</color>
+
+ <!-- Background color for sync-off alert. -->
+ <color name="alert_background">#e0e0e0</color>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2c911c9..1ef1bd5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1573,12 +1573,6 @@
<!--Label of the "default account" setting option to set default editor account. [CHAR LIMIT=80]-->
<string name="default_editor_account">Default account for new contacts</string>
- <!--Label of the "Sync contact metadata" setting option to set sync account for Lychee. [CHAR LIMIT=80]-->
- <string name="sync_contact_metadata_title">Sync contact metadata [DOGFOOD]</string>
-
- <!--Label of the "Sync contact metadata" setting dialog to set sync account for Lychee. [CHAR LIMIT=80]-->
- <string name="sync_contact_metadata_dialog_title">Sync contact metadata</string>
-
<!-- Title of my info preference, showing the name of user's personal profile [CHAR LIMIT=30]-->
<string name="settings_my_info_title">My info</string>
@@ -1819,4 +1813,24 @@
<!-- Content description of the cancel navigation icon shown in SIM import dialog toolbar -->
<string name="sim_import_cancel_content_description">Cancel import</string>
+ <!-- Alert for letting user know that their device auto-sync setting is turned off,
+ in case they are wondering why they are not seeing any contact. [CHAR LIMIT=150] -->
+ <string name="auto_sync_off">Auto-sync is off. Tap to turn on.</string>
+
+ <!-- Content description for the "X" image icon for dismissing an alert.[CHAR LIMIT=50] -->
+ <string name="dismiss_sync_alert">Dismiss</string>
+
+ <!-- Alert for letting user know that their account level sync setting is turned off,
+ in case they are wondering why they are not seeing any contact. [CHAR LIMIT=150] -->
+ <string name="account_sync_off">Account sync is off. Tap to turn on.</string>
+
+ <!-- Title of dialog to turn auto-sync on [CHAR LIMIT=100] -->
+ <string name="turn_auto_sync_on_dialog_title">Turn auto-sync on?</string>
+
+ <!-- Text of dialog to turn auto-sync on [CHAR LIMIT=500] -->
+ <string name="turn_auto_sync_on_dialog_body">Changes you make to all apps and accounts,
+ not just Contacts, will be synchronized between the web and your devices.</string>
+
+ <!-- Confirm button text for dialog to turn auto-sync on [CHAR LIMIT=30] -->
+ <string name="turn_auto_sync_on_dialog_confirm_btn">Turn on</string>
</resources>
diff --git a/src-bind/com/android/contactsbind/ObjectFactory.java b/src-bind/com/android/contactsbind/ObjectFactory.java
index c9539ee..d6799e2 100644
--- a/src-bind/com/android/contactsbind/ObjectFactory.java
+++ b/src-bind/com/android/contactsbind/ObjectFactory.java
@@ -14,7 +14,6 @@
package com.android.contactsbind;
import com.android.contacts.common.logging.Logger;
-import com.android.contacts.common.preference.PreferenceManager;
import com.android.contactsbind.search.AutocompleteHelper;
import com.android.contacts.common.util.DeviceLocalAccountTypeFactory;
@@ -32,8 +31,6 @@
return null;
}
- public static PreferenceManager getPreferenceManager(Context context) { return null; }
-
public static AutocompleteHelper getAutocompleteHelper(Context context) {
return null;
}
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 9326b0d..0d4b4c0 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -173,9 +173,7 @@
// the syncs is in progress.
if (syncableAccounts != null && syncableAccounts.size() > 0) {
for (Account account: syncableAccounts) {
- if (SyncUtil.isSyncStatusPendingOrActive(account)
- || SyncUtil.isUnsyncableGoogleAccount(account)) {
- swipeRefreshLayout.setRefreshing(true);
+ if (SyncUtil.isSyncStatusPendingOrActive(account)) {
return;
}
}
diff --git a/src/com/android/contacts/common/Experiments.java b/src/com/android/contacts/common/Experiments.java
index a8120f6..5de4d5d 100644
--- a/src/com/android/contacts/common/Experiments.java
+++ b/src/com/android/contacts/common/Experiments.java
@@ -58,6 +58,12 @@
public static final String PULL_TO_REFRESH = "PullToRefresh__pull_to_refresh";
/**
+ * Flags for maximum time to show spinner for a contacts sync.
+ */
+ public static final String PULL_TO_REFRESH_CANCEL_REFRESH_MILLIS =
+ "PullToRefresh__cancel_refresh_millis";
+
+ /**
* Search study boolean indicating whether to inject yenta search results before CP2 results.
*/
public static final String SEARCH_YENTA = "Search__yenta";
diff --git a/src/com/android/contacts/common/compat/MetadataSyncEnabledCompat.java b/src/com/android/contacts/common/compat/MetadataSyncEnabledCompat.java
deleted file mode 100644
index 4a9650f..0000000
--- a/src/com/android/contacts/common/compat/MetadataSyncEnabledCompat.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.contacts.common.compat;
-
-import android.content.Context;
-import android.provider.Settings;
-
-public class MetadataSyncEnabledCompat {
- public static boolean isMetadataSyncEnabled(Context context) {
- return CompatUtils.isNCompatible()
- ? (Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.CONTACT_METADATA_SYNC_ENABLED, 0) == 1)
- : false;
- }
-}
\ No newline at end of file
diff --git a/src/com/android/contacts/common/list/CustomContactListFilterActivity.java b/src/com/android/contacts/common/list/CustomContactListFilterActivity.java
index b961b67..157d0a8 100644
--- a/src/com/android/contacts/common/list/CustomContactListFilterActivity.java
+++ b/src/com/android/contacts/common/list/CustomContactListFilterActivity.java
@@ -143,7 +143,10 @@
final AccountSet accounts = new AccountSet();
- final List<AccountWithDataSet> sourceAccounts = accountTypes.getAccounts(false);
+ // Don't include the null account because it doesn't support writing to
+ // ContactsContract.Settings
+ final List<AccountWithDataSet> sourceAccounts = accountTypes.getAccounts(
+ AccountTypeManager.nonNullAccountFilter());
final AccountDisplayInfoFactory displayableAccountFactory =
new AccountDisplayInfoFactory(context, sourceAccounts);
for (AccountWithDataSet account : sourceAccounts) {
diff --git a/src/com/android/contacts/common/model/AccountTypeManager.java b/src/com/android/contacts/common/model/AccountTypeManager.java
index 705f5bf..d91e73b 100644
--- a/src/com/android/contacts/common/model/AccountTypeManager.java
+++ b/src/com/android/contacts/common/model/AccountTypeManager.java
@@ -61,6 +61,8 @@
import com.android.contactsbind.ObjectFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -76,6 +78,8 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
+import javax.annotation.Nullable;
+
import static com.android.contacts.common.util.DeviceLocalAccountTypeFactory.Util.isLocalAccountType;
/**
@@ -128,6 +132,11 @@
}
@Override
+ public List<AccountWithDataSet> getAccounts(Predicate<AccountWithDataSet> filter) {
+ return Collections.emptyList();
+ }
+
+ @Override
public List<AccountWithDataSet> getGroupWritableAccounts() {
return Collections.emptyList();
}
@@ -166,6 +175,8 @@
// TODO: Consider splitting this into getContactWritableAccounts() and getAllAccounts()
public abstract List<AccountWithDataSet> getAccounts(boolean contactWritableOnly);
+ public abstract List<AccountWithDataSet> getAccounts(Predicate<AccountWithDataSet> filter);
+
public abstract List<AccountWithDataSet> getSortedAccounts(AccountWithDataSet defaultAccount,
boolean contactWritableOnly);
@@ -272,6 +283,14 @@
return canGetAccounts && canReadContacts;
}
+ public static Predicate<AccountWithDataSet> nonNullAccountFilter() {
+ return new Predicate<AccountWithDataSet>() {
+ @Override
+ public boolean apply(@Nullable AccountWithDataSet account) {
+ return account != null && account.name != null && account.type != null;
+ }
+ };
+ }
}
class AccountComparator implements Comparator<AccountWithDataSet> {
@@ -755,6 +774,11 @@
return Lists.newArrayList(contactWritableOnly ? mContactWritableAccounts : mAccounts);
}
+ @Override
+ public List<AccountWithDataSet> getAccounts(Predicate<AccountWithDataSet> filter) {
+ return new ArrayList<>(Collections2.filter(mAccounts, filter));
+ }
+
/**
* Return list of all known or contact writable {@link AccountWithDataSet}'s sorted by
* {@code defaultAccount}.
diff --git a/src/com/android/contacts/common/preference/ContactsPreferences.java b/src/com/android/contacts/common/preference/ContactsPreferences.java
index 26073f4..8865e91 100644
--- a/src/com/android/contacts/common/preference/ContactsPreferences.java
+++ b/src/com/android/contacts/common/preference/ContactsPreferences.java
@@ -16,7 +16,6 @@
package com.android.contacts.common.preference;
-import android.accounts.Account;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
@@ -27,7 +26,6 @@
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
-import android.provider.ContactsContract;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.support.annotation.NonNull;
@@ -36,10 +34,7 @@
import com.android.contacts.common.R;
import com.android.contacts.common.model.account.AccountWithDataSet;
-import com.android.contacts.common.model.account.GoogleAccountType;
-import com.android.contacts.common.model.AccountTypeManager;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -75,14 +70,6 @@
public static final boolean PREF_DISPLAY_ONLY_PHONES_DEFAULT = false;
- public static final String DO_NOT_SYNC_CONTACT_METADATA_MSG = "Do not sync metadata";
-
- public static final String CONTACT_METADATA_AUTHORITY = "com.android.contacts.metadata";
-
- public static final String SHOULD_CLEAR_METADATA_BEFORE_SYNCING =
- "should_clear_metadata_before_syncing";
-
- public static final String ONLY_CLEAR_DONOT_SYNC = "only_clear_donot_sync";
/**
* Value to use when a preference is unassigned and needs to be read from the shared preferences
*/
@@ -248,74 +235,6 @@
return false;
}
- public String getContactMetadataSyncAccountName() {
- final Account syncAccount = getContactMetadataSyncAccount();
- return syncAccount == null ? DO_NOT_SYNC_CONTACT_METADATA_MSG : syncAccount.name;
- }
-
- public void setContactMetadataSyncAccount(AccountWithDataSet accountWithDataSet) {
- final String mContactMetadataSyncAccount =
- accountWithDataSet == null ? null : accountWithDataSet.name;
- requestMetadataSyncForAccount(mContactMetadataSyncAccount);
- }
-
- private Account getContactMetadataSyncAccount() {
- for (Account account : getFocusGoogleAccounts()) {
- if (ContentResolver.getIsSyncable(account, CONTACT_METADATA_AUTHORITY) == 1
- && ContentResolver.getSyncAutomatically(account, CONTACT_METADATA_AUTHORITY)) {
- return account;
- }
- }
- return null;
- }
-
- /**
- * Turn on contact metadata sync for this {@param accountName} and turn off automatic sync
- * for other accounts. If accountName is null, then turn off automatic sync for all accounts.
- */
- private void requestMetadataSyncForAccount(String accountName) {
- for (Account account : getFocusGoogleAccounts()) {
- if (!TextUtils.isEmpty(accountName) && accountName.equals(account.name)) {
- // Request sync.
- final Bundle b = new Bundle();
- b.putBoolean(SHOULD_CLEAR_METADATA_BEFORE_SYNCING, true);
- b.putBoolean(ONLY_CLEAR_DONOT_SYNC, false);
- b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
- b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
- ContentResolver.requestSync(account, CONTACT_METADATA_AUTHORITY, b);
-
- ContentResolver.setSyncAutomatically(account, CONTACT_METADATA_AUTHORITY, true);
- } else if (ContentResolver.getSyncAutomatically(account, CONTACT_METADATA_AUTHORITY)) {
- // Turn off automatic sync for previous sync account.
- ContentResolver.setSyncAutomatically(account, CONTACT_METADATA_AUTHORITY, false);
- if (TextUtils.isEmpty(accountName)) {
- // Request sync to clear old data.
- final Bundle b = new Bundle();
- b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
- b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
- b.putBoolean(SHOULD_CLEAR_METADATA_BEFORE_SYNCING, true);
- b.putBoolean(ONLY_CLEAR_DONOT_SYNC, true);
- ContentResolver.requestSync(account, CONTACT_METADATA_AUTHORITY, b);
- }
- }
- }
- }
-
- /**
- * @return google accounts with "com.google" account type and null data set.
- */
- private List<Account> getFocusGoogleAccounts() {
- List<Account> focusGoogleAccounts = new ArrayList<Account>();
- final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(mContext);
- List<AccountWithDataSet> accounts = accountTypeManager.getAccounts(true);
- for (AccountWithDataSet account : accounts) {
- if (GoogleAccountType.ACCOUNT_TYPE.equals(account.type) && account.dataSet == null) {
- focusGoogleAccounts.add(account.getAccountOrNull());
- }
- }
- return focusGoogleAccounts;
- }
-
public void registerChangeListener(ChangeListener listener) {
if (mListener != null) unregisterChangeListener();
diff --git a/src/com/android/contacts/common/preference/DisplayOptionsPreferenceFragment.java b/src/com/android/contacts/common/preference/DisplayOptionsPreferenceFragment.java
index b3b2832..a6881ed 100644
--- a/src/com/android/contacts/common/preference/DisplayOptionsPreferenceFragment.java
+++ b/src/com/android/contacts/common/preference/DisplayOptionsPreferenceFragment.java
@@ -57,7 +57,6 @@
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.common.util.AccountFilterUtil;
import com.android.contacts.common.util.ImplicitIntentsUtil;
-import com.android.contactsbind.ObjectFactory;
import java.util.List;
@@ -207,7 +206,6 @@
mAreContactsAvailable = args.getBoolean(ARG_CONTACTS_AVAILABLE);
removeUnsupportedPreferences();
- addExtraPreferences();
mMyInfoPreference = findPreference(KEY_MY_INFO);
@@ -291,16 +289,6 @@
}
}
- private void addExtraPreferences() {
- final PreferenceManager preferenceManager = ObjectFactory.getPreferenceManager(
- getContext());
- if (preferenceManager != null) {
- for (Preference preference : preferenceManager.getPreferences()) {
- getPreferenceScreen().addPreference(preference);
- }
- }
- }
-
@Override
public Context getContext() {
return getActivity();
diff --git a/src/com/android/contacts/common/preference/PreferenceManager.java b/src/com/android/contacts/common/preference/PreferenceManager.java
deleted file mode 100644
index 816f94e..0000000
--- a/src/com/android/contacts/common/preference/PreferenceManager.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.contacts.common.preference;
-
-import android.preference.Preference;
-
-import java.util.List;
-
-public interface PreferenceManager {
- List<Preference> getPreferences();
-}
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index c8da194..bf4f891 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -92,7 +92,7 @@
private boolean mSelectionVerified;
private int mLastSelectedPosition = -1;
private boolean mRefreshingContactUri;
- protected ContactListFilter mFilter;
+ private ContactListFilter mFilter;
private String mPersistentSelectionPrefix = PERSISTENT_SELECTION_PREFIX;
protected OnContactBrowserActionListener mListener;
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 25315e8..a748c06 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -26,10 +26,12 @@
import android.content.Loader;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Directory;
import android.support.v4.widget.SwipeRefreshLayout;
@@ -88,7 +90,8 @@
* Fragment containing a contact list used for browsing (as compared to
* picking a contact with one of the PICK intents).
*/
-public class DefaultContactBrowseListFragment extends ContactBrowseListFragment {
+public class DefaultContactBrowseListFragment extends ContactBrowseListFragment
+ implements EnableGlobalSyncDialogFragment.Listener {
private static final String TAG = "DefaultListFragment";
private static final String ENABLE_DEBUG_OPTIONS_HIDDEN_CODE = "debug debug!";
@@ -102,7 +105,22 @@
private View mEmptyHomeView;
private View mAccountFilterContainer;
private TextView mSearchProgressText;
+
private SwipeRefreshLayout mSwipeRefreshLayout;
+ private final Handler mHandler = new Handler();
+ private final Runnable mCancelRefresh = new Runnable() {
+ @Override
+ public void run() {
+ if (mSwipeRefreshLayout.isRefreshing()) {
+ mSwipeRefreshLayout.setRefreshing(false);
+ }
+ }
+ };
+
+ private View mAlertContainer;
+ private TextView mAlertText;
+ private ImageView mAlertDismissIcon;
+ private int mReasonSyncOff = SyncUtil.SYNC_SETTING_SYNC_ON;
private boolean mContactsAvailable;
private boolean mEnableDebugMenuOptions;
@@ -164,6 +182,10 @@
maybeHideCheckBoxes();
mActivity.invalidateOptionsMenu();
mActivity.showFabWithAnimation(/* showFab */ true);
+
+ // Alert user if sync is off and not dismissed before
+ setSyncOffAlert();
+
// Determine whether the account has pullToRefresh feature
if (Flags.getInstance(getContext()).getBoolean(Experiments.PULL_TO_REFRESH)) {
setSwipeRefreshLayoutEnabledOrNot(getFilter());
@@ -452,6 +474,66 @@
mSearchProgress = getView().findViewById(R.id.search_progress);
mSearchProgressText = (TextView) mSearchHeaderView.findViewById(R.id.totalContactsText);
+
+ mAlertContainer = getView().findViewById(R.id.alert_container);
+ mAlertText = (TextView) mAlertContainer.findViewById(R.id.alert_text);
+ mAlertDismissIcon = (ImageView) mAlertContainer.findViewById(R.id.alert_dismiss_icon);
+ mAlertText.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ turnSyncOn();
+ }
+ });
+ mAlertDismissIcon.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dismiss();
+ }
+ });
+
+ mAlertContainer.setVisibility(View.GONE);
+ }
+
+ private void turnSyncOn() {
+ final ContactListFilter filter = getFilter();
+ if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT
+ && mReasonSyncOff == SyncUtil.SYNC_SETTING_ACCOUNT_SYNC_OFF) {
+ ContentResolver.setSyncAutomatically(
+ new Account(filter.accountName, filter.accountType),
+ ContactsContract.AUTHORITY, true);
+ mAlertContainer.setVisibility(View.GONE);
+ } else {
+ final EnableGlobalSyncDialogFragment dialog = new
+ EnableGlobalSyncDialogFragment();
+ dialog.show(this, filter);
+ }
+ }
+
+ @Override
+ public void onEnableAutoSync(ContactListFilter filter) {
+ // Turn on auto-sync
+ ContentResolver.setMasterSyncAutomatically(true);
+ // Also enable Contacts sync
+ final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(
+ getContext()).getAccounts(/* contactsWritableOnly */ true);
+ final List<Account> syncableAccounts = filter.getSyncableAccounts(accounts);
+ if (syncableAccounts != null && syncableAccounts.size() > 0) {
+ for (Account account : syncableAccounts) {
+ ContentResolver.setSyncAutomatically(new Account(account.name, account.type),
+ ContactsContract.AUTHORITY, true);
+ }
+ }
+ mAlertContainer.setVisibility(View.GONE);
+ }
+
+ private void dismiss() {
+ if (mReasonSyncOff == SyncUtil.SYNC_SETTING_GLOBAL_SYNC_OFF) {
+ SharedPreferenceUtil.incNumOfDismissesForAutoSyncOff(getContext());
+ } else if (mReasonSyncOff == SyncUtil.SYNC_SETTING_ACCOUNT_SYNC_OFF) {
+ SharedPreferenceUtil.incNumOfDismissesForAccountSyncOff(
+ getContext(), getFilter().accountName);
+ }
+ mAlertContainer.setVisibility(View.GONE);
}
private void initSwipeRefreshLayout() {
@@ -465,7 +547,10 @@
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
- syncContacts(mFilter);
+ mHandler.removeCallbacks(mCancelRefresh);
+ syncContacts(getFilter());
+ mHandler.postDelayed(mCancelRefresh, Flags.getInstance(getContext())
+ .getInteger(Experiments.PULL_TO_REFRESH_CANCEL_REFRESH_MILLIS));
}
});
mSwipeRefreshLayout.setColorSchemeResources(
@@ -503,6 +588,36 @@
}
}
+ private void setSyncOffAlert() {
+ final ContactListFilter filter = getFilter();
+ final Account account = filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT
+ && filter.isGoogleAccountType()
+ ? new Account(filter.accountName, filter.accountType) : null;
+
+ if (account == null && !filter.isContactsFilterType()) {
+ mAlertContainer.setVisibility(View.GONE);
+ } else {
+ mReasonSyncOff = SyncUtil.calculateReasonSyncOff(getContext(), account);
+ final boolean isAlertVisible =
+ SyncUtil.isAlertVisible(getContext(), account, mReasonSyncOff);
+ setSyncOffMsg(mReasonSyncOff);
+ mAlertContainer.setVisibility(isAlertVisible ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ private void setSyncOffMsg(int reason) {
+ final Resources resources = getResources();
+ switch (reason) {
+ case SyncUtil.SYNC_SETTING_GLOBAL_SYNC_OFF:
+ mAlertText.setText(resources.getString(R.string.auto_sync_off));
+ break;
+ case SyncUtil.SYNC_SETTING_ACCOUNT_SYNC_OFF:
+ mAlertText.setText(resources.getString(R.string.account_sync_off));
+ break;
+ default:
+ }
+ }
+
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@@ -702,6 +817,9 @@
updateListFilter(filter, restoreSelectedUri);
mActivity.setTitle(AccountFilterUtil.getActionBarTitleForFilter(mActivity, filter));
+ // Alert user if sync is off and not dismissed before
+ setSyncOffAlert();
+
// Determine whether the account has pullToRefresh feature
if (Flags.getInstance(getContext()).getBoolean(Experiments.PULL_TO_REFRESH)) {
setSwipeRefreshLayoutEnabledOrNot(filter);
diff --git a/src/com/android/contacts/list/EnableGlobalSyncDialogFragment.java b/src/com/android/contacts/list/EnableGlobalSyncDialogFragment.java
new file mode 100644
index 0000000..46df4ae
--- /dev/null
+++ b/src/com/android/contacts/list/EnableGlobalSyncDialogFragment.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.list;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import com.android.contacts.R;
+import com.android.contacts.common.list.ContactListFilter;
+
+/**
+ * Confirmation dialog for turning global auto-sync setting on.
+ */
+public class EnableGlobalSyncDialogFragment extends DialogFragment{
+
+ private static final String ARG_FILTER = "filter";
+ private ContactListFilter mFilter;
+
+ /**
+ * Callbacks for the dialog host.
+ */
+ public interface Listener {
+
+ /**
+ * Invoked after the user has confirmed that they want to turn on sync.
+ *
+ * @param filter the filter of current contacts list.
+ */
+ void onEnableAutoSync(ContactListFilter filter);
+ }
+
+ public static void show(DefaultContactBrowseListFragment fragment, ContactListFilter filter) {
+ final Bundle args = new Bundle();
+ args.putParcelable(ARG_FILTER, filter);
+
+ final EnableGlobalSyncDialogFragment dialog = new
+ EnableGlobalSyncDialogFragment();
+ dialog.setTargetFragment(fragment, 0);
+ dialog.setArguments(args);
+ dialog.show(fragment.getFragmentManager(), "globalSync");
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mFilter = getArguments().getParcelable(ARG_FILTER);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Listener targetListener = (Listener) getTargetFragment();
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle(R.string.turn_auto_sync_on_dialog_title)
+ .setMessage(R.string.turn_auto_sync_on_dialog_body)
+ .setPositiveButton(R.string.turn_auto_sync_on_dialog_confirm_btn,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (targetListener != null) {
+ targetListener.onEnableAutoSync(mFilter);
+ }
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.setCancelable(false);
+ return builder.create();
+ }
+}
diff --git a/src/com/android/contacts/util/SharedPreferenceUtil.java b/src/com/android/contacts/util/SharedPreferenceUtil.java
index e33c7aa..3adbcc4 100644
--- a/src/com/android/contacts/util/SharedPreferenceUtil.java
+++ b/src/com/android/contacts/util/SharedPreferenceUtil.java
@@ -30,6 +30,12 @@
private static final String PREFERENCE_KEY_HAMBURGER_PROMO_TRIGGER_ACTION_HAPPENED_BEFORE =
"hamburgerPromoTriggerActionHappenedBefore";
+ public static final String PREFERENCE_KEY_GLOBAL_SYNC_OFF_DISMISSES =
+ "num-of-dismisses-auto-sync-off";
+
+ public static final String PREFERENCE_KEY_ACCOUNT_SYNC_OFF_DISMISSES
+ = "num-of-dismisses-account-sync-off";
+
public static boolean getHamburgerPromoDisplayedBefore(Context context) {
return getSharedPreferences(context)
.getBoolean(PREFERENCE_KEY_HAMBURGER_PROMO_DISPLAYED_BEFORE, false);
@@ -81,4 +87,48 @@
private static SharedPreferences getSharedPreferences(Context context) {
return context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
}
+
+ public static int getNumOfDismissesForAutoSyncOff(Context context) {
+ return getSharedPreferences(context).getInt(PREFERENCE_KEY_GLOBAL_SYNC_OFF_DISMISSES, 0);
+ }
+
+ public static void resetNumOfDismissesForAutoSyncOff(Context context) {
+ final int value = getSharedPreferences(context).getInt(
+ PREFERENCE_KEY_GLOBAL_SYNC_OFF_DISMISSES, 0);
+ if (value != 0) {
+ getSharedPreferences(context).edit()
+ .putInt(PREFERENCE_KEY_GLOBAL_SYNC_OFF_DISMISSES, 0).apply();
+ }
+ }
+
+ public static void incNumOfDismissesForAutoSyncOff(Context context) {
+ final int value = getSharedPreferences(context).getInt(
+ PREFERENCE_KEY_GLOBAL_SYNC_OFF_DISMISSES, 0);
+ getSharedPreferences(context).edit()
+ .putInt(PREFERENCE_KEY_GLOBAL_SYNC_OFF_DISMISSES, value + 1).apply();
+ }
+
+ private static String buildSharedPrefsName(String accountName) {
+ return accountName + "-" + PREFERENCE_KEY_ACCOUNT_SYNC_OFF_DISMISSES;
+ }
+
+ public static int getNumOfDismissesforAccountSyncOff(Context context, String accountName) {
+ return getSharedPreferences(context).getInt(buildSharedPrefsName(accountName), 0);
+ }
+
+ public static void resetNumOfDismissesForAccountSyncOff(Context context, String accountName) {
+ final int value = getSharedPreferences(context).getInt(
+ buildSharedPrefsName(accountName), 0);
+ if (value != 0) {
+ getSharedPreferences(context).edit()
+ .putInt(buildSharedPrefsName(accountName), 0).apply();
+ }
+ }
+
+ public static void incNumOfDismissesForAccountSyncOff(Context context, String accountName) {
+ final int value = getSharedPreferences(context).getInt(
+ buildSharedPrefsName(accountName), 0);
+ getSharedPreferences(context).edit()
+ .putInt(buildSharedPrefsName(accountName), value + 1).apply();
+ }
}
diff --git a/src/com/android/contacts/util/SyncUtil.java b/src/com/android/contacts/util/SyncUtil.java
index cef2223..6c17c05 100644
--- a/src/com/android/contacts/util/SyncUtil.java
+++ b/src/com/android/contacts/util/SyncUtil.java
@@ -17,6 +17,7 @@
import android.accounts.Account;
import android.content.ContentResolver;
+import android.content.Context;
import android.provider.ContactsContract;
import com.android.contacts.common.model.account.GoogleAccountType;
@@ -29,6 +30,10 @@
public final class SyncUtil {
private static final String TAG = "SyncUtil";
+ public static final int SYNC_SETTING_SYNC_ON = 0;
+ public static final int SYNC_SETTING_GLOBAL_SYNC_OFF = 1;
+ public static final int SYNC_SETTING_ACCOUNT_SYNC_OFF = 2;
+
private SyncUtil() {
}
@@ -49,4 +54,40 @@
}
return ContentResolver.getIsSyncable(account, ContactsContract.AUTHORITY) <= 0;
}
-}
+
+ public static boolean isAlertVisible(Context context, Account account, int reason) {
+ if (reason == SYNC_SETTING_GLOBAL_SYNC_OFF) {
+ return (SharedPreferenceUtil.getNumOfDismissesForAutoSyncOff(context) == 0);
+ } else if (reason == SYNC_SETTING_ACCOUNT_SYNC_OFF && account != null) {
+ return (SharedPreferenceUtil.getNumOfDismissesforAccountSyncOff(
+ context, account.name) == 0);
+ }
+ return false;
+ }
+
+ public static int calculateReasonSyncOff(Context context, Account account) {
+ // Global sync is turned off
+ if (!ContentResolver.getMasterSyncAutomatically()) {
+ if (account != null) {
+ SharedPreferenceUtil.resetNumOfDismissesForAccountSyncOff(
+ context, account.name);
+ }
+ return SYNC_SETTING_GLOBAL_SYNC_OFF;
+ }
+
+ // Global sync is on, clear the number of times users has dismissed this
+ // alert so that next time global sync is off, alert gets displayed again.
+ SharedPreferenceUtil.resetNumOfDismissesForAutoSyncOff(context);
+ if (account != null) {
+ // Account level sync is off
+ if (!ContentResolver.getSyncAutomatically(account, ContactsContract.AUTHORITY)) {
+ return SYNC_SETTING_ACCOUNT_SYNC_OFF;
+ }
+ // Account sync is on, clear the number of times users has dismissed this
+ // alert so that next time sync is off, alert gets displayed again.
+ SharedPreferenceUtil.resetNumOfDismissesForAccountSyncOff(
+ context, account.name);
+ }
+ return SYNC_SETTING_SYNC_ON;
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java b/tests/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java
index f4ec238..b701b3a 100644
--- a/tests/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java
+++ b/tests/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java
@@ -23,6 +23,8 @@
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.common.model.account.BaseAccountType;
import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -68,6 +70,11 @@
}
@Override
+ public List<AccountWithDataSet> getAccounts(Predicate<AccountWithDataSet> filter) {
+ return Lists.newArrayList(Collections2.filter(Arrays.asList(mAccounts), filter));
+ }
+
+ @Override
public List<AccountWithDataSet> getSortedAccounts(AccountWithDataSet account,
boolean writableOnly) {
return Arrays.asList(mAccounts);