Set up a sync preference and policy for syncing [2]

- Adds a preference for enabling sync, which controls the sync behavior
- Make the ProductionFlags depend on appropriate flags to guarantee that
  we don't mess things when flipping some flags
- Preferences now control the "syncable" property of the provider
  thereby controlling the policy and when this entry shows up in
  system settings.

Bug: 17464069
Change-Id: I1d58351188518c1ae9f1f9e147b5ea15d32a3427
diff --git a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java
index 10fc612..99b9589 100644
--- a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java
+++ b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java
@@ -46,5 +46,5 @@
     /**
      * When {@code true}, personal dictionary sync feature is ready to be enabled.
      */
-    public static final boolean ENABLE_PERSONAL_DICTIONARY_SYNC = false;
+    public static final boolean ENABLE_PERSONAL_DICTIONARY_SYNC = ENABLE_ACCOUNT_SIGN_IN && false;
 }
diff --git a/java-overridable/src/com/android/inputmethod/latin/sync/BeanstalkManager.java b/java-overridable/src/com/android/inputmethod/latin/sync/BeanstalkManager.java
deleted file mode 100644
index 2242d92..0000000
--- a/java-overridable/src/com/android/inputmethod/latin/sync/BeanstalkManager.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 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.inputmethod.latin.sync;
-
-import android.content.Context;
-
-import javax.annotation.Nonnull;
-import javax.annotation.concurrent.GuardedBy;
-
-public class BeanstalkManager {
-    private static final Object sLock = new Object();
-
-    @GuardedBy("sLock")
-    private static BeanstalkManager sInstance;
-
-    /**
-     * @return the singleton instance of {@link BeanstalkManager}.
-     */
-    @Nonnull
-    public static BeanstalkManager getInstance(Context context) {
-        synchronized(sLock) {
-            if (sInstance == null) {
-                sInstance = new BeanstalkManager(context.getApplicationContext());
-            }
-        }
-        return sInstance;
-    }
-
-    private BeanstalkManager(final Context context) {
-        // Intentional private constructor for singleton.
-    }
-
-    public void onCreate() {
-    }
-
-    public void requestSync() {
-    }
-
-    public void onDestroy() {
-    }
-}
\ No newline at end of file
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 6aea637..ed05e91 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -56,9 +56,11 @@
     <!--  Option for enabling or disabling the split keyboard layout. [CHAR LIMIT=65]-->
     <string name="enable_split_keyboard">Enable split keyboard</string>
 
-    <string name="sync_now_title" translatable="false">Sync Now</string>
-    <string name="sync_now_summary" translatable="false">Sync your personal dictionary</string>
-    <string name="sync_now_summary_disabled_signed_out" translatable="false">Select an account to enable sync</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>
 
     <!-- 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>
diff --git a/java/res/xml/prefs_screen_accounts.xml b/java/res/xml/prefs_screen_accounts.xml
index 003a37f..41642bf 100644
--- a/java/res/xml/prefs_screen_accounts.xml
+++ b/java/res/xml/prefs_screen_accounts.xml
@@ -28,7 +28,15 @@
         android:title="@string/switch_accounts"
         android:summary="@string/no_accounts_selected" />
 
-    <!-- title will be set programmatically to embed application name -->
+    <!-- Summary will be set programmatically to reflect the account status -->
+    <CheckBoxPreference
+        android:key="pref_enable_cloud_sync"
+        android:title="@string/cloud_sync_title"
+        android:defaultValue="false"
+        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"
@@ -38,5 +46,6 @@
     <!-- This preference (acts like a button) enables the user to initiate an one time sync. -->
     <Preference android:key="pref_beanstalk"
         android:persistent="false"
-        android:title="@string/sync_now_title" />
+        android:title="@string/sync_now_title"
+        android:dependency="pref_enable_cloud_sync" />
 </PreferenceScreen>
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 743b570..3b995f9 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -87,7 +87,6 @@
 import com.android.inputmethod.latin.settings.SettingsValues;
 import com.android.inputmethod.latin.suggestions.SuggestionStripView;
 import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
-import com.android.inputmethod.latin.sync.BeanstalkManager;
 import com.android.inputmethod.latin.touchinputconsumer.GestureConsumer;
 import com.android.inputmethod.latin.utils.ApplicationUtils;
 import com.android.inputmethod.latin.utils.CapsModeUtils;
@@ -561,7 +560,6 @@
         AudioAndHapticFeedbackManager.init(this);
         AccessibilityUtils.init(this);
         mStatsUtilsManager.onCreate(this /* context */);
-        BeanstalkManager.getInstance(this /* context */).onCreate();
         super.onCreate();
 
         mHandler.onCreate();
@@ -709,7 +707,6 @@
         unregisterReceiver(mDictionaryPackInstallReceiver);
         unregisterReceiver(mDictionaryDumpBroadcastReceiver);
         mStatsUtilsManager.onDestroy();
-        BeanstalkManager.getInstance(this /* context */).onDestroy();
         super.onDestroy();
     }
 
diff --git a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java
index fa71645..c382426 100644
--- a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java
@@ -16,7 +16,12 @@
 
 package com.android.inputmethod.latin.settings;
 
+import static com.android.inputmethod.latin.settings.LocalSettingsConstants.PREF_ACCOUNT_NAME;
+import static com.android.inputmethod.latin.settings.LocalSettingsConstants.PREF_ENABLE_CLOUD_SYNC;
+
+import android.accounts.Account;
 import android.app.AlertDialog;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.SharedPreferences;
@@ -33,7 +38,6 @@
 import com.android.inputmethod.latin.SubtypeSwitcher;
 import com.android.inputmethod.latin.accounts.LoginAccountUtils;
 import com.android.inputmethod.latin.define.ProductionFlags;
-import com.android.inputmethod.latin.sync.BeanstalkManager;
 
 import javax.annotation.Nullable;
 
@@ -41,19 +45,18 @@
  * "Accounts & Privacy" settings sub screen.
  *
  * This settings sub screen handles the following preferences:
- * <li> Account selection/management for IME
- * <li> TODO: Sync preferences
- * <li> TODO: Privacy preferences
- * <li> Sync now
+ * <li> Account selection/management for IME </li>
+ * <li> Sync preferences </li>
+ * <li> Privacy preferences </li>
  */
 public final class AccountsSettingsFragment extends SubScreenFragment {
-    static final String PREF_ACCCOUNT_SWITCHER = "account_switcher";
-    static final String PREF_SYNC_NOW = "pref_beanstalk";
+    private static final String PREF_SYNC_NOW = "pref_beanstalk";
 
-    private final DialogInterface.OnClickListener mAccountSelectedListener =
-            new AccountSelectedListener();
-    private final DialogInterface.OnClickListener mAccountSignedOutListener =
-            new AccountSignedOutListener();
+    @UsedForTesting static final String AUTHORITY = "com.android.inputmethod.latin.provider";
+    static final String PREF_ACCCOUNT_SWITCHER = "account_switcher";
+
+    private final DialogInterface.OnClickListener mAccountChangedListener =
+            new AccountChangedListener();
     private final Preference.OnPreferenceClickListener mSyncNowListener = new SyncNowListener();
 
     @Override
@@ -81,47 +84,55 @@
             removePreference(Settings.PREF_ENABLE_METRICS_LOGGING);
         }
 
+        if (!ProductionFlags.ENABLE_ACCOUNT_SIGN_IN) {
+            removePreference(PREF_ACCCOUNT_SWITCHER);
+            removePreference(PREF_ENABLE_CLOUD_SYNC);
+            removePreference(PREF_SYNC_NOW);
+        }
         if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) {
+            removePreference(PREF_ENABLE_CLOUD_SYNC);
             removePreference(PREF_SYNC_NOW);
         } else {
             final Preference syncNowPreference = findPreference(PREF_SYNC_NOW);
-            if (syncNowPreference != null) {
-                syncNowPreference.setOnPreferenceClickListener(mSyncNowListener);
-            }
+            syncNowPreference.setOnPreferenceClickListener(mSyncNowListener);
         }
     }
 
     @Override
     public void onResume() {
         super.onResume();
-        refreshUi();
+        refreshAccountAndDependentPreferences(getCurrentlySelectedAccount());
     }
 
     @Override
     public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
-        // TODO: Look at the preference that changed before refreshing the view.
-        refreshUi();
+        if (TextUtils.equals(key, PREF_ACCOUNT_NAME)) {
+            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);
+            updateSyncPolicy(syncEnabled, LoginAccountUtils.getCurrentAccount(getActivity()));
+        }
     }
 
-    private void refreshUi() {
-        refreshAccountSelection();
-        refreshSyncNow();
-    }
-
-    private void refreshAccountSelection() {
+    private void refreshAccountAndDependentPreferences(@Nullable final String currentAccount) {
         if (!ProductionFlags.ENABLE_ACCOUNT_SIGN_IN) {
             return;
         }
 
-        final String currentAccount = getCurrentlySelectedAccount();
         final Preference accountSwitcher = findPreference(PREF_ACCCOUNT_SWITCHER);
         if (currentAccount == null) {
             // No account is currently selected.
             accountSwitcher.setSummary(getString(R.string.no_accounts_selected));
+            // Disable the sync preference UI.
+            disableSyncPreference();
         } else {
             // Set the currently selected account.
             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);
         accountSwitcher.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@@ -129,39 +140,64 @@
             public boolean onPreferenceClick(Preference preference) {
                 if (accountsForLogin.length == 0) {
                     // TODO: Handle account addition.
-                    Toast.makeText(getActivity(),
-                            getString(R.string.account_select_cancel), Toast.LENGTH_SHORT).show();
+                    Toast.makeText(getActivity(), getString(R.string.account_select_cancel),
+                            Toast.LENGTH_SHORT).show();
                 } else {
                     createAccountPicker(accountsForLogin, currentAccount).show();
                 }
                 return true;
             }
         });
-
-        // TODO: Depending on the account selection, enable/disable preferences that
-        // depend on an account.
     }
 
     /**
-     * Refreshes the "Sync Now" feature
+     * Enables the Sync preference UI and updates its summary.
      */
-    private void refreshSyncNow() {
+    private void enableSyncPreference() {
         if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) {
             return;
         }
 
-        final Preference syncNowPreference = findPreference(PREF_SYNC_NOW);
-        if (syncNowPreference == null) {
+        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_PERSONAL_DICTIONARY_SYNC) {
             return;
         }
 
-        final String currentAccount = getCurrentlySelectedAccount();
-        if (currentAccount == null) {
-            syncNowPreference.setEnabled(false);
-            syncNowPreference.setSummary(R.string.sync_now_summary_disabled_signed_out);
+        final Preference syncPreference = findPreference(PREF_ENABLE_CLOUD_SYNC);
+        syncPreference.setEnabled(false);
+        syncPreference.setSummary(R.string.cloud_sync_summary_disabled_signed_out);
+    }
+
+    /**
+     * Given a non-null accountToUse, this method looks at the enabled value to either
+     * set or unset the syncable property of the sync authority.
+     * If the account is null, this method is a no-op currently, but we may want
+     * to perform some cleanup in the future.
+     */
+    @UsedForTesting
+    void updateSyncPolicy(boolean enabled, Account accountToUse) {
+        if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) {
+            return;
+        }
+
+        if (accountToUse != null) {
+            final int syncable = enabled ? 1 : 0;
+            ContentResolver.setIsSyncable(accountToUse, AUTHORITY, syncable);
+            // TODO: Also add a periodic sync here.
+            // See ContentResolver.addPeriodicSync
         } else {
-            syncNowPreference.setEnabled(true);
-            syncNowPreference.setSummary(R.string.sync_now_summary);
+            // Without an account, we cannot really set the sync to off.
+            // Hopefully the account sign-out listener would have taken care of that for us.
+            // But cases such as clear data are still not handled cleanly.
         }
     }
 
@@ -170,6 +206,10 @@
         return getSharedPreferences().getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null);
     }
 
+    private boolean isSyncEnabled() {
+        return getSharedPreferences().getBoolean(PREF_ENABLE_CLOUD_SYNC, false);
+    }
+
     /**
      * Creates an account picker dialog showing the given accounts in a list and selecting
      * the selected account by default.
@@ -200,51 +240,55 @@
         final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
                 .setTitle(R.string.account_select_title)
                 .setSingleChoiceItems(accounts, index, null)
-                .setPositiveButton(R.string.account_select_ok, mAccountSelectedListener)
+                .setPositiveButton(R.string.account_select_ok, mAccountChangedListener)
                 .setNegativeButton(R.string.account_select_cancel, null);
         if (isSignedIn) {
-            builder.setNeutralButton(R.string.account_select_sign_out, mAccountSignedOutListener);
+            builder.setNeutralButton(R.string.account_select_sign_out, mAccountChangedListener);
         }
         return builder.create();
     }
 
     /**
-     * Listener for an account being selected from the picker.
-     * Persists the account to shared preferences.
+     * Listener for a account selection changes from the picker.
+     * Persists/removes the account to/from shared preferences and sets up sync if required.
      */
-    class AccountSelectedListener implements DialogInterface.OnClickListener {
+    class AccountChangedListener implements DialogInterface.OnClickListener {
         @Override
         public void onClick(DialogInterface dialog, int which) {
-            final ListView lv = ((AlertDialog)dialog).getListView();
-            final Object selectedItem = lv.getItemAtPosition(lv.getCheckedItemPosition());
-            getSharedPreferences()
-                    .edit()
-                    .putString(LocalSettingsConstants.PREF_ACCOUNT_NAME, (String) selectedItem)
-                    .apply();
+            switch (which) {
+                case DialogInterface.BUTTON_POSITIVE: // Signed in
+                    final ListView lv = ((AlertDialog)dialog).getListView();
+                    final Object selectedItem = lv.getItemAtPosition(lv.getCheckedItemPosition());
+                    getSharedPreferences()
+                            .edit()
+                            .putString(PREF_ACCOUNT_NAME, (String) selectedItem)
+                            .apply();
+                    // Attempt starting sync for the new account if sync was
+                    // previously enabled.
+                    // If not, stop it.
+                    updateSyncPolicy(isSyncEnabled(),
+                            LoginAccountUtils.getCurrentAccount(getActivity()));
+                    break;
+                case DialogInterface.BUTTON_NEUTRAL: // Signed out
+                    // Stop sync for the account that's being signed out of.
+                    updateSyncPolicy(false, LoginAccountUtils.getCurrentAccount(getActivity()));
+                    getSharedPreferences()
+                            .edit()
+                            .remove(PREF_ACCOUNT_NAME)
+                            .apply();
+                    break;
+            }
         }
     }
 
     /**
-     * Listener for sign-out being initiated from from the picker.
-     * Removed the account from shared preferences.
-     */
-    class AccountSignedOutListener implements DialogInterface.OnClickListener {
-        @Override
-        public void onClick(DialogInterface dialog, int which) {
-            getSharedPreferences()
-                    .edit()
-                    .remove(LocalSettingsConstants.PREF_ACCOUNT_NAME)
-                    .apply();
-        }
-    }
-
-    /**
-     * Listener that initates the process of sync in the background.
+     * Listener that initiates the process of sync in the background.
      */
     class SyncNowListener implements Preference.OnPreferenceClickListener {
         @Override
         public boolean onPreferenceClick(final Preference preference) {
-            BeanstalkManager.getInstance(getActivity() /* context */).requestSync();
+            ContentResolver.requestSync(
+                    LoginAccountUtils.getCurrentAccount(getActivity()), AUTHORITY, Bundle.EMPTY);
             return true;
         }
     }
diff --git a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
index 71d6065..f4f2e03 100644
--- a/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
+++ b/java/src/com/android/inputmethod/latin/settings/LocalSettingsConstants.java
@@ -27,6 +27,9 @@
     // Preference key for the current account.
     // Do not restore.
     public static final String PREF_ACCOUNT_NAME = "pref_account_name";
+    // Preference key for enabling cloud sync feature.
+    // Do not restore.
+    public static final String PREF_ENABLE_CLOUD_SYNC = "pref_enable_cloud_sync";
 
     // List of preference keys to skip from being restored by backup agent.
     // These preferences are tied to a device and hence should not be restored.
@@ -36,6 +39,7 @@
     // shared preferences which makes it non-trivial to move these out to
     // a different shared preferences file.
     public static final String[] PREFS_TO_SKIP_RESTORING = new String[] {
-        PREF_ACCOUNT_NAME
+        PREF_ACCOUNT_NAME,
+        PREF_ENABLE_CLOUD_SYNC
     };
 }
diff --git a/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java
index 2ef8b54..a0b2c22 100644
--- a/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java
+++ b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java
@@ -16,14 +16,20 @@
 
 package com.android.inputmethod.latin.settings;
 
+import static com.android.inputmethod.latin.settings.AccountsSettingsFragment.AUTHORITY;
+
+import android.accounts.Account;
 import android.app.AlertDialog;
 import android.app.Dialog;
+import android.content.ContentResolver;
 import android.content.Intent;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 import android.widget.ListView;
 
+import com.android.inputmethod.latin.define.ProductionFlags;
+
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -32,6 +38,7 @@
         extends ActivityInstrumentationTestCase2<TestFragmentActivity> {
     private static final String FRAG_NAME = AccountsSettingsFragment.class.getName();
     private static final long TEST_TIMEOUT_MILLIS = 5000;
+    private static final Account TEST_ACCOUNT = new Account("account-for-test", "account-type");
 
     private AlertDialog mDialog;
 
@@ -47,6 +54,13 @@
         setActivityIntent(intent);
     }
 
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        // reset the syncable state to unknown
+        ContentResolver.setIsSyncable(TEST_ACCOUNT, AUTHORITY, -1);
+    }
+
     public void testEmptyAccounts() {
         final AccountsSettingsFragment fragment =
                 (AccountsSettingsFragment) getActivity().mFragment;
@@ -129,4 +143,57 @@
         assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_NEGATIVE).getVisibility());
         assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_POSITIVE).getVisibility());
     }
+
+    public void testUpdateSyncPolicy_enable() {
+        // This test is a no-op when ENABLE_PERSONAL_DICTIONARY_SYNC is not enabled
+        if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) {
+            return;
+        }
+        // Should be unknown by default.
+        assertTrue(ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY) < 0);
+
+        final AccountsSettingsFragment fragment =
+                (AccountsSettingsFragment) getActivity().mFragment;
+        fragment.updateSyncPolicy(true, TEST_ACCOUNT);
+
+        // Should be syncable now.
+        assertEquals(1, ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY));
+    }
+
+    public void testUpdateSyncPolicy_disable() {
+        // This test is a no-op when ENABLE_PERSONAL_DICTIONARY_SYNC is not enabled
+        if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) {
+            return;
+        }
+        // Should be unknown by default.
+        assertTrue(ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY) < 0);
+
+        final AccountsSettingsFragment fragment =
+                (AccountsSettingsFragment) getActivity().mFragment;
+        fragment.updateSyncPolicy(false, TEST_ACCOUNT);
+
+        // Should not be syncable now.
+        assertEquals(0, ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY));
+    }
+
+    public void testUpdateSyncPolicy_enableDisable() {
+        // This test is a no-op when ENABLE_PERSONAL_DICTIONARY_SYNC is not enabled
+        if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) {
+            return;
+        }
+        // Should be unknown by default.
+        assertTrue(ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY) < 0);
+
+        final AccountsSettingsFragment fragment =
+                (AccountsSettingsFragment) getActivity().mFragment;
+        fragment.updateSyncPolicy(true, TEST_ACCOUNT);
+
+        // Should be syncable now.
+        assertEquals(1, ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY));
+
+        fragment.updateSyncPolicy(false, TEST_ACCOUNT);
+
+        // Should not be syncable now.
+        assertEquals(0, ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY));
+    }
 }