Show sync-off alert am: 491cca5773
am: bcb07594eb
Change-Id: I4be709cf51d7053d4af2f9adffb0c7945f334d36
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 e644960..3537e54 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1822,4 +1822,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/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index c8fb863..6e1b7e4 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -26,6 +26,7 @@
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.graphics.drawable.Drawable;
@@ -95,7 +96,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!";
@@ -111,6 +113,11 @@
private TextView mSearchProgressText;
private SwipeRefreshLayout mSwipeRefreshLayout;
+ private View mAlertContainer;
+ private TextView mAlertText;
+ private ImageView mAlertDismissIcon;
+ private int mReasonSyncOff = SyncUtil.SYNC_SETTING_SYNC_ON;
+
private boolean mContactsAvailable;
private boolean mEnableDebugMenuOptions;
private boolean mIsRecreatedInstance;
@@ -172,6 +179,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());
@@ -461,6 +472,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() {
@@ -512,6 +583,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);
@@ -711,6 +812,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