From 8ec328fb2e454c2c9e353b6aec0bc613a1846c88 Mon Sep 17 00:00:00 2001
From: Jatin Matani <jatinm@google.com>
Date: Tue, 24 Feb 2015 09:59:02 -0800
Subject: [PATCH] Refresh pref settings for cloud sync

- Show the opt-in text
- Show the account picker if user presses 'enable sync'. Previously
  we disabled the sync pref which was confusing.
- Remove the debug tags from some prefs; and improve messaging overall

General rules;
- sync is turned ON : user checks 'enable sync' pref AND
  accepts the opt-in AND chooses an account
- sync is turned OFF: when user signs out

Demo link
https://drive.google.com/a/google.com/file/d/0B9tNQOWdRuiWSUdVVE5rVDJudlk/view?usp=sharing

Change-Id: I2e7933796b15e47005ba9970a8c1294416ef31a0
---
 java/res/values/strings.xml                   |  42 ++--
 java/res/xml/prefs_screen_accounts.xml        |  11 +-
 .../settings/AccountsSettingsFragment.java    | 219 +++++++++++-------
 .../AccountsSettingsFragmentTests.java        |   6 +-
 4 files changed, 169 insertions(+), 109 deletions(-)

diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 2e9c8ea1f6..50aa4a782d 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -39,11 +39,9 @@
     <!-- Settings screen title for preferences [CHAR LIMIT=33]-->
     <string name="settings_screen_preferences">Preferences</string>
     <!-- Settings screen title for accounts and privacy preferences [CHAR LIMIT=33]-->
-    <string name="settings_screen_accounts">Accounts &amp; privacy</string>
+    <string name="settings_screen_accounts">Accounts &amp; Privacy</string>
     <!-- Settings screen title for appearance & layouts preferences [CHAR LIMIT=33] -->
-    <string name="settings_screen_appearance">Appearance &amp; layouts</string>
-    <!-- Settings screen title for multilingual options [CHAR_LIMIT=33] -->
-    <string name="settings_screen_multilingual">Multilingual options</string>
+    <string name="settings_screen_appearance">Appearance &amp; Layouts</string>
     <!-- Settings screen title for gesture typing preferences [CHAR_LIMIT=33] -->
     <string name="settings_screen_gesture">Gesture Typing</string>
     <!-- Settings screen title for text correction options [CHAR_LIMIT=33] -->
@@ -56,17 +54,31 @@
     <!--  Option for enabling or disabling the split keyboard layout. [CHAR LIMIT=65]-->
     <string name="enable_split_keyboard">Enable split keyboard</string>
 
-    <!-- TODO: Enable translation for user-visible strings -->
-    <string name="cloud_sync_title" translatable="false">Enable sync</string>
-    <string name="cloud_sync_summary" translatable="false">Sync your personal dictionary across devices</string>
-    <string name="cloud_sync_summary_disabled_signed_out" translatable="false">Select an account to enable sync</string>
-    <string name="sync_now_title" translatable="false">[DEBUG] Sync Now</string>
-    <string name="clear_sync_data_title" translatable="false">[DEBUG] Delete Google Keyboard cloud data</string>
-    <string name="clear_sync_data_summary" translable="false">Deletes your synced data from Google</string>
-    <string name="clear_sync_data_confirmation" translable="false">Your synced data will be deleted. Are you sure?</string>
-    <string name="clear_sync_data_ok" translable="false">Delete</string>
-    <string name="clear_sync_data_cancel" translable="false">Cancel</string>
-
+    <!-- Option title for enabling cloud sync feature [CHAR LIMIT=33]-->
+    <string name="cloud_sync_title">Google Keyboard Sync</string>
+    <!-- Option summary when cloud sync feature is enabled  [CHAR LIMIT=65] -->
+    <string name="cloud_sync_summary">Sync is turned on</string>
+    <!-- Option summary when cloud sync feature is disabled  [CHAR LIMIT=65] -->
+    <string name="cloud_sync_summary_disabled">Sync your personal dictionary across devices</string>
+    <!-- Option title for starting the sync cycle now. [CHAR LIMIT=33]-->
+    <string name="sync_now_title">Sync Now</string>
+    <!-- Option title for letting user delete data from Google servers.  [CHAR LIMIT=33] -->
+    <string name="clear_sync_data_title">Delete Keyboard Cloud data</string>
+    <!-- Option summary for letting user delete data from Google servers. [CHAR LIMIT=65] -->
+    <string name="clear_sync_data_summary">Deletes your synced data from Google</string>
+    <!-- Text for confirmation dialog box asking user to confirm deletion of cloud data. [CHAR LIMIT=65] -->
+    <string name="clear_sync_data_confirmation">Your synced data will be deleted from the cloud. Are you sure?</string>
+    <!-- Option to confirm deleting of user data from cloud [CHAR LIMIT=20] -->
+    <string name="clear_sync_data_ok">Delete</string>
+    <!-- Option to cancel the deletion of user data from cloud [CHAR LIMIT=20] -->
+    <string name="cloud_sync_cancel">Cancel</string>
+    <!-- Option to agree to terms and conditions for enabling cloud sync feature. -->
+    <string name="cloud_sync_opt_in_text">Your personal dictionary will be synced &amp; backed up to
+        Google servers. The statistical information of word frequency may be collected to help 
+        improve our products. The collection and usage of all the information will be compliant with
+        <a href="https://www.google.com/policies/privacy">Google\'s Privacy Policy</a>.
+    </string>
+    
     <!-- Option name for including other IMEs in the language switch list [CHAR LIMIT=30] -->
     <string name="include_other_imes_in_language_switch_list">Switch to other input methods</string>
     <!-- Option summary for including other IMEs in the language switch list [CHAR LIMIT=65] -->
diff --git a/java/res/xml/prefs_screen_accounts.xml b/java/res/xml/prefs_screen_accounts.xml
index 4624885476..e4baf79847 100644
--- a/java/res/xml/prefs_screen_accounts.xml
+++ b/java/res/xml/prefs_screen_accounts.xml
@@ -36,21 +36,14 @@
         android:persistent="true"
         android:disableDependentsState="false" />
 
-    <!-- Title will be set programmatically to embed application name -->
-    <CheckBoxPreference
-        android:key="pref_enable_metrics_logging"
-        android:summary="@string/enable_metrics_logging_summary"
-        android:defaultValue="true"
-        android:persistent="true" />
-
     <!-- This preference (acts like a button) enables the user to initiate an one time sync. -->
-    <Preference android:key="pref_beanstalk"
+    <Preference android:key="pref_sync_now"
         android:persistent="false"
         android:title="@string/sync_now_title"
         android:dependency="pref_enable_cloud_sync" />
 
     <!-- This preference (acts like a button) enables the user to clear data from the cloud. -->
-    <Preference android:key="pref_beanstalk_clear_data"
+    <Preference android:key="pref_clear_sync_data"
         android:persistent="false"
         android:title="@string/clear_sync_data_title"
         android:summary="@string/clear_sync_data_summary"
diff --git a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java
index 10d570cbf8..3a48260435 100644
--- a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java
@@ -23,8 +23,8 @@ import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.SharedPreferences;
-import android.content.res.Resources;
 import android.os.Bundle;
+import android.preference.CheckBoxPreference;
 import android.preference.Preference;
 import android.preference.Preference.OnPreferenceClickListener;
 import android.text.TextUtils;
@@ -48,36 +48,50 @@ import javax.annotation.Nullable;
  * <li> Privacy preferences </li>
  */
 public final class AccountsSettingsFragment extends SubScreenFragment {
-    private static final String PREF_SYNC_NOW = "pref_beanstalk";
-    private static final String PREF_CLEAR_SYNC_DATA = "pref_beanstalk_clear_data";
+    private static final String PREF_ENABLE_SYNC_NOW = "pref_enable_cloud_sync";
+    private static final String PREF_SYNC_NOW = "pref_sync_now";
+    private static final String PREF_CLEAR_SYNC_DATA = "pref_clear_sync_data";
 
     static final String PREF_ACCCOUNT_SWITCHER = "account_switcher";
 
-    private final DialogInterface.OnClickListener mAccountChangedListener =
-            new AccountChangedListener();
-    private final Preference.OnPreferenceClickListener mSyncNowListener = new SyncNowListener();
-    private final Preference.OnPreferenceClickListener mClearSyncDataListener =
-            new ClearSyncDataListener();
+    /**
+     * Onclick listener for sync now pref.
+     */
+    private final Preference.OnPreferenceClickListener mSyncNowListener =
+            new SyncNowListener();
+    /**
+     * Onclick listener for delete sync pref.
+     */
+    private final Preference.OnPreferenceClickListener mDeleteSyncDataListener =
+            new DeleteSyncDataListener();
+
+    /**
+     * Onclick listener for enable sync pref.
+     */
+    private final Preference.OnPreferenceClickListener mEnableSyncClickListener =
+            new EnableSyncClickListener();
+
+    /**
+     * Enable sync checkbox pref.
+     */
+    private CheckBoxPreference mEnableSyncPreference;
+
+    /**
+     * Enable sync checkbox pref.
+     */
+    private Preference mSyncNowPreference;
+
+    /**
+     * Clear sync data pref.
+     */
+    private Preference mClearSyncDataPreference;
+
 
     @Override
     public void onCreate(final Bundle icicle) {
         super.onCreate(icicle);
         addPreferencesFromResource(R.xml.prefs_screen_accounts);
 
-        final Resources res = getResources();
-
-        if (ProductionFlags.IS_METRICS_LOGGING_SUPPORTED) {
-            final Preference enableMetricsLogging =
-                    findPreference(Settings.PREF_ENABLE_METRICS_LOGGING);
-            if (enableMetricsLogging != null) {
-                final String enableMetricsLoggingTitle = res.getString(
-                        R.string.enable_metrics_logging, getApplicationName());
-                enableMetricsLogging.setTitle(enableMetricsLoggingTitle);
-            }
-        } else {
-            removePreference(Settings.PREF_ENABLE_METRICS_LOGGING);
-        }
-
         if (!ProductionFlags.ENABLE_ACCOUNT_SIGN_IN) {
             removePreference(PREF_ACCCOUNT_SWITCHER);
             removePreference(PREF_ENABLE_CLOUD_SYNC);
@@ -89,11 +103,14 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
             removePreference(PREF_SYNC_NOW);
             removePreference(PREF_CLEAR_SYNC_DATA);
         } else {
-            final Preference syncNowPreference = findPreference(PREF_SYNC_NOW);
-            syncNowPreference.setOnPreferenceClickListener(mSyncNowListener);
+            mEnableSyncPreference = (CheckBoxPreference) findPreference(PREF_ENABLE_SYNC_NOW);
+            mEnableSyncPreference.setOnPreferenceClickListener(mEnableSyncClickListener);
+
+            mSyncNowPreference = findPreference(PREF_SYNC_NOW);
+            mSyncNowPreference.setOnPreferenceClickListener(mSyncNowListener);
 
-            final Preference clearSyncDataPreference = findPreference(PREF_CLEAR_SYNC_DATA);
-            clearSyncDataPreference.setOnPreferenceClickListener(mClearSyncDataListener);
+            mClearSyncDataPreference = findPreference(PREF_CLEAR_SYNC_DATA);
+            mClearSyncDataPreference.setOnPreferenceClickListener(mDeleteSyncDataListener);
         }
     }
 
@@ -106,15 +123,25 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
     @Override
     public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
         if (TextUtils.equals(key, PREF_ACCOUNT_NAME)) {
-            refreshAccountAndDependentPreferences(
-                    prefs.getString(PREF_ACCOUNT_NAME, null));
+            refreshAccountAndDependentPreferences(prefs.getString(PREF_ACCOUNT_NAME, null));
         } else if (TextUtils.equals(key, PREF_ENABLE_CLOUD_SYNC)) {
             final boolean syncEnabled = prefs.getBoolean(PREF_ENABLE_CLOUD_SYNC, false);
-            AccountStateChangedListener.onSyncPreferenceChanged(
-                    getSignedInAccountName(), syncEnabled);
+            mEnableSyncPreference = (CheckBoxPreference)findPreference(PREF_ENABLE_SYNC_NOW);
+            mEnableSyncPreference.setChecked(syncEnabled);
+            if (syncEnabled) {
+                mEnableSyncPreference.setSummary(R.string.cloud_sync_summary);
+            } else {
+                mEnableSyncPreference.setSummary(R.string.cloud_sync_summary_disabled);
+            }
+            AccountStateChangedListener.onSyncPreferenceChanged(getSignedInAccountName(),
+                    syncEnabled);
         }
     }
 
+    /**
+     * Summarizes what account is being used and turns off dependent preferences if no account
+     * is currently selected.
+     */
     private void refreshAccountAndDependentPreferences(@Nullable final String currentAccount) {
         if (!ProductionFlags.ENABLE_ACCOUNT_SIGN_IN) {
             return;
@@ -122,61 +149,33 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
 
         final Preference accountSwitcher = findPreference(PREF_ACCCOUNT_SWITCHER);
         if (currentAccount == null) {
-            // No account is currently selected.
+            // No account is currently selected; switch enable sync preference off.
             accountSwitcher.setSummary(getString(R.string.no_accounts_selected));
-            // Disable the sync preference UI.
-            disableSyncPreference();
+            mEnableSyncPreference.setChecked(false);
         } else {
-            // Set the currently selected account.
+            // Set the currently selected account as the summary text.
             accountSwitcher.setSummary(getString(R.string.account_selected, currentAccount));
-            // Enable the sync preference UI.
-            enableSyncPreference();
         }
-        // Set up onClick listener for the account picker preference.
-        final Context context = getActivity();
-        final String[] accountsForLogin = LoginAccountUtils.getAccountsForLogin(context);
+
+        // Set up on click listener for the account picker preference.
         accountSwitcher.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-            @Override
-            public boolean onPreferenceClick(final Preference preference) {
-                if (accountsForLogin.length == 0) {
-                    // TODO: Handle account addition.
-                    Toast.makeText(getActivity(), getString(R.string.account_select_cancel),
-                            Toast.LENGTH_SHORT).show();
-                } else {
-                    createAccountPicker(accountsForLogin, currentAccount).show();
+                @Override
+                public boolean onPreferenceClick(final Preference preference) {
+                    final String[] accountsForLogin =
+                            LoginAccountUtils.getAccountsForLogin(getActivity());
+                    if (accountsForLogin.length == 0) {
+                        // TODO: Handle account addition.
+                        Toast.makeText(getActivity(), getString(R.string.account_select_cancel),
+                                Toast.LENGTH_SHORT).show();
+                    } else {
+                        createAccountPicker(accountsForLogin, currentAccount,
+                                new AccountChangedListener(null)).show();
+                    }
+                    return true;
                 }
-                return true;
-            }
         });
     }
 
-    /**
-     * Enables the Sync preference UI and updates its summary.
-     */
-    private void enableSyncPreference() {
-        if (!ProductionFlags.ENABLE_USER_HISTORY_DICTIONARY_SYNC) {
-            return;
-        }
-
-        final Preference syncPreference = findPreference(PREF_ENABLE_CLOUD_SYNC);
-        syncPreference.setEnabled(true);
-        syncPreference.setSummary(R.string.cloud_sync_summary);
-    }
-
-    /**
-     * Disables the Sync preference UI and updates its summary to indicate
-     * the fact that an account needs to be selected for sync.
-     */
-    private void disableSyncPreference() {
-        if (!ProductionFlags.ENABLE_USER_HISTORY_DICTIONARY_SYNC) {
-            return;
-        }
-
-        final Preference syncPreference = findPreference(PREF_ENABLE_CLOUD_SYNC);
-        syncPreference.setEnabled(false);
-        syncPreference.setSummary(R.string.cloud_sync_summary_disabled_signed_out);
-    }
-
     @Nullable
     String getSignedInAccountName() {
         return getSharedPreferences().getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null);
@@ -188,14 +187,19 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
 
     /**
      * Creates an account picker dialog showing the given accounts in a list and selecting
-     * the selected account by default.
-     * The list of accounts must not be null/empty.
+     * the selected account by default.  The list of accounts must not be null/empty.
      *
      * Package-private for testing.
+     *
+     * @param accounts list of accounts on the device.
+     * @param selectedAccount currently selected account
+     * @param positiveButtonClickListener listener that gets called when positive button is
+     * clicked
      */
     @UsedForTesting
     AlertDialog createAccountPicker(final String[] accounts,
-            final String selectedAccount) {
+            final String selectedAccount,
+            final DialogInterface.OnClickListener positiveButtonClickListener) {
         if (accounts == null || accounts.length == 0) {
             throw new IllegalArgumentException("List of accounts must not be empty");
         }
@@ -216,10 +220,10 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
         final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
                 .setTitle(R.string.account_select_title)
                 .setSingleChoiceItems(accounts, index, null)
-                .setPositiveButton(R.string.account_select_ok, mAccountChangedListener)
+                .setPositiveButton(R.string.account_select_ok, positiveButtonClickListener)
                 .setNegativeButton(R.string.account_select_cancel, null);
         if (isSignedIn) {
-            builder.setNeutralButton(R.string.account_select_sign_out, mAccountChangedListener);
+            builder.setNeutralButton(R.string.account_select_sign_out, positiveButtonClickListener);
         }
         return builder.create();
     }
@@ -229,6 +233,15 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
      * Persists/removes the account to/from shared preferences and sets up sync if required.
      */
     class AccountChangedListener implements DialogInterface.OnClickListener {
+        /**
+         * Represents preference that should be changed based on account chosen.
+         */
+        private CheckBoxPreference mDependentPreference;
+
+        AccountChangedListener(final CheckBoxPreference dependentPreference) {
+            mDependentPreference = dependentPreference;
+        }
+
         @Override
         public void onClick(final DialogInterface dialog, final int which) {
             final String oldAccount = getSignedInAccountName();
@@ -242,6 +255,9 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
                             .putString(PREF_ACCOUNT_NAME, newAccount)
                             .apply();
                     AccountStateChangedListener.onAccountSignedIn(oldAccount, newAccount);
+                    if (mDependentPreference != null) {
+                        mDependentPreference.setChecked(true);
+                    }
                     break;
                 case DialogInterface.BUTTON_NEUTRAL: // Signed out
                     AccountStateChangedListener.onAccountSignedOut(oldAccount);
@@ -268,7 +284,7 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
     /**
      * Listener that initiates the process of deleting user's data from the cloud.
      */
-    class ClearSyncDataListener implements Preference.OnPreferenceClickListener {
+    class DeleteSyncDataListener implements Preference.OnPreferenceClickListener {
         @Override
         public boolean onPreferenceClick(final Preference preference) {
             final AlertDialog confirmationDialog = new AlertDialog.Builder(getActivity())
@@ -290,4 +306,43 @@ public final class AccountsSettingsFragment extends SubScreenFragment {
             return true;
         }
     }
+
+    /**
+     * Listens to events when user clicks on "Enable sync" feature.
+     */
+    class EnableSyncClickListener implements Preference.OnPreferenceClickListener {
+        @Override
+        public boolean onPreferenceClick(final Preference preference) {
+            final CheckBoxPreference syncPreference = (CheckBoxPreference) preference;
+            if (syncPreference.isChecked()) {
+                // Uncheck for now.
+                syncPreference.setChecked(false);
+
+                // Show opt-in.
+                final AlertDialog optInDialog = new AlertDialog.Builder(getActivity())
+                        .setTitle(R.string.cloud_sync_title)
+                        .setMessage(R.string.cloud_sync_opt_in_text)
+                        .setPositiveButton(R.string.account_select_ok,
+                                new DialogInterface.OnClickListener() {
+                                    @Override
+                                    public void onClick(final DialogInterface dialog,
+                                            final int which) {
+                                        if (which == DialogInterface.BUTTON_POSITIVE) {
+                                            final Context context = getActivity();
+                                            final String[] accountsForLogin =
+                                                    LoginAccountUtils.getAccountsForLogin(context);
+                                            createAccountPicker(accountsForLogin,
+                                                    getSignedInAccountName(),
+                                                    new AccountChangedListener(syncPreference))
+                                                    .show();
+                                        }
+                                    }
+                         })
+                         .setNegativeButton(R.string.cloud_sync_cancel, null)
+                         .create();
+                optInDialog.show();
+            }
+            return true;
+        }
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java
index 36e967275b..81a0f6934e 100644
--- a/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java
+++ b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java
@@ -49,7 +49,7 @@ public class AccountsSettingsFragmentTests
         final AccountsSettingsFragment fragment =
                 (AccountsSettingsFragment) getActivity().mFragment;
         try {
-            fragment.createAccountPicker(new String[0], null);
+            fragment.createAccountPicker(new String[0], null, null /* listener */);
             fail("Expected IllegalArgumentException, never thrown");
         } catch (IllegalArgumentException expected) {
             // Expected.
@@ -76,7 +76,7 @@ public class AccountsSettingsFragmentTests
                                 "2@example.com",
                                 "3@example.com",
                                 "4@example.com"},
-                        null);
+                        null, null /* positiveButtonListner */);
                 dialog.show();
                 dialogHolder.mDialog = dialog;
                 latch.countDown();
@@ -118,7 +118,7 @@ public class AccountsSettingsFragmentTests
                                 "2@example.com",
                                 "3@example.com",
                                 "4@example.com"},
-                        "3@example.com");
+                        "3@example.com", null /* positiveButtonListner */);
                 dialog.show();
                 dialogHolder.mDialog = dialog;
                 latch.countDown();
-- 
GitLab