diff options
author | dsmyers@chromium.org <dsmyers@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-19 04:36:54 +0000 |
---|---|---|
committer | dsmyers@chromium.org <dsmyers@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-19 04:36:54 +0000 |
commit | 4385014499032789eb4db19a4ce3d34403dca62e (patch) | |
tree | fb2549a9cddbf3cbfe53c8730a46f34a6d1068b4 /sync | |
parent | b040f9da1e83507fcff6588bacdcb7e298734940 (diff) | |
download | chromium_src-4385014499032789eb4db19a4ce3d34403dca62e.zip chromium_src-4385014499032789eb4db19a4ce3d34403dca62e.tar.gz chromium_src-4385014499032789eb4db19a4ce3d34403dca62e.tar.bz2 |
Add an invalidation preferences wrapper.
Adds an InvalidationPreferences class that manages the preferences used by the
sync code to store data, such as the sync types on which notifications should
be received.
BUG=159221
Review URL: https://chromiumcodereview.appspot.com/11969049
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@177835 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sync')
5 files changed, 281 insertions, 11 deletions
diff --git a/sync/android/java/src/org/chromium/sync/internal_api/pub/base/ModelType.java b/sync/android/java/src/org/chromium/sync/internal_api/pub/base/ModelType.java index 4dbdab0..2d309be 100644 --- a/sync/android/java/src/org/chromium/sync/internal_api/pub/base/ModelType.java +++ b/sync/android/java/src/org/chromium/sync/internal_api/pub/base/ModelType.java @@ -4,14 +4,19 @@ package org.chromium.sync.internal_api.pub.base; +import android.util.Log; + +import com.google.common.collect.Sets; import com.google.ipc.invalidation.external.client.types.ObjectId; import com.google.protos.ipc.invalidation.Types; +import java.util.Collection; +import java.util.Set; + /** * The model types that are synced in Chrome for Android. */ public enum ModelType { - /** * A bookmark folder or a bookmark URL object. */ @@ -25,6 +30,11 @@ public enum ModelType { */ SESSION("SESSION"); + /** Special type representing all possible types. */ + public static final String ALL_TYPES_TYPE = "ALL_TYPES"; + + private static final String TAG = ModelType.class.getSimpleName(); + private final String mModelType; ModelType(String modelType) { @@ -43,4 +53,31 @@ public enum ModelType { return null; } } + + /** + * Converts string representations of types to sync to {@link ModelType}s. + * <p> + * If {@code syncTypes} contains {@link #ALL_TYPES_TYPE}, then the returned + * set contains all values of the {@code ModelType} enum. + * <p> + * Otherwise, the returned set contains the {@code ModelType} values for all elements of + * {@code syncTypes} for which {@link ModelType#valueOf(String)} successfully returns; other + * elements are dropped. + */ + public static Set<ModelType> syncTypesToModelTypes(Collection<String> syncTypes) { + if (syncTypes.contains(ALL_TYPES_TYPE)) { + return Sets.newHashSet(ModelType.values()); + } else { + Set<ModelType> modelTypes = Sets.newHashSetWithExpectedSize(syncTypes.size()); + for (String syncType : syncTypes) { + try { + modelTypes.add(ModelType.valueOf(syncType)); + } catch (IllegalArgumentException exception) { + // Drop invalid sync types. + Log.w(TAG, "Could not translate sync type to model type: " + syncType); + } + } + return modelTypes; + } + } } diff --git a/sync/android/java/src/org/chromium/sync/notifier/InvalidationController.java b/sync/android/java/src/org/chromium/sync/notifier/InvalidationController.java index 71ac7b5..df7f14f 100644 --- a/sync/android/java/src/org/chromium/sync/notifier/InvalidationController.java +++ b/sync/android/java/src/org/chromium/sync/notifier/InvalidationController.java @@ -12,6 +12,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.util.Log; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; @@ -19,6 +20,8 @@ import org.chromium.sync.internal_api.pub.base.ModelType; import java.util.Set; +import javax.annotation.Nullable; + /** * Controller used to send start, stop, and registration-change commands to the invalidation * client library used by Sync. @@ -36,11 +39,6 @@ public class InvalidationController { "org.chromium.sync.notifier.ACTION_REGISTER_TYPES"; /** - * Special syncable type that lets us know to sync all types. - */ - public static final String ALL_TYPES_TYPE = "ALL_TYPES"; - - /** * Parcelable-valued intent extra containing the account of the user. */ public static final String EXTRA_ACCOUNT = "account"; @@ -64,7 +62,7 @@ public class InvalidationController { Intent registerIntent = new Intent(ACTION_REGISTER); String[] selectedTypesArray; if (allTypes) { - selectedTypesArray = new String[]{ALL_TYPES_TYPE}; + selectedTypesArray = new String[]{ModelType.ALL_TYPES_TYPE}; } else { selectedTypesArray = new String[types.size()]; int pos = 0; @@ -156,6 +154,15 @@ public class InvalidationController { * @return {@code intent} */ private Intent setDestinationClassName(Intent intent) { + String className = getDestinationClassName(context); + if (className != null) { + intent.setClassName(context, className); + } + return intent; + } + + @VisibleForTesting + @Nullable static String getDestinationClassName(Context context) { ApplicationInfo appInfo; try { // Fetch application info and read the appropriate metadata element. @@ -168,12 +175,11 @@ public class InvalidationController { if (className == null) { Log.wtf(TAG, "No value for " + IMPLEMENTING_CLASS_MANIFEST_PROPERTY + " in manifest; sync notifications will not work"); - } else { - intent.setClassName(context, className); } + return className; } catch (NameNotFoundException exception) { Log.wtf(TAG, "Cannot read own application info", exception); } - return intent; + return null; } } diff --git a/sync/android/java/src/org/chromium/sync/notifier/InvalidationPreferences.java b/sync/android/java/src/org/chromium/sync/notifier/InvalidationPreferences.java new file mode 100644 index 0000000..43eec81 --- /dev/null +++ b/sync/android/java/src/org/chromium/sync/notifier/InvalidationPreferences.java @@ -0,0 +1,121 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.sync.notifier; + +import android.accounts.Account; +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.util.Log; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import javax.annotation.Nullable; + +/** + * Class to manage the preferences used by the invalidation client. + * <p> + * This class provides methods to read and write the preferences used by the invalidation client. + * <p> + * To read a preference, call the appropriate {@code get...} method. + * <p> + * To write a preference, first call {@link #edit} to obtain a {@link EditContext}. Then, make + * one or more calls to a {@code set...} method, providing the same edit context to each call. + * Finally, call {@link #commit(EditContext)} to save the changes to stable storage. + * + * @author dsmyers@google.com (Daniel Myers) + */ +public class InvalidationPreferences { + /** + * Wrapper around a {@link android.content.SharedPreferences.Editor} for the preferences. + * Used to avoid exposing raw preference objects to users of this class. + */ + public class EditContext { + private final SharedPreferences.Editor editor; + + EditContext() { + this.editor = PreferenceManager.getDefaultSharedPreferences(mContext).edit(); + } + } + + @VisibleForTesting + public static class PrefKeys { + /** + * Shared preference key to store the invalidation types that we want to register + * for. + */ + @VisibleForTesting + public static final String SYNC_TANGO_TYPES = "sync_tango_types"; + + /** Shared preference key to store the name of the account in use. */ + @VisibleForTesting + public static final String SYNC_ACCT_NAME = "sync_acct_name"; + + /** Shared preference key to store the type of account in use. */ + static final String SYNC_ACCT_TYPE = "sync_acct_type"; + } + + private static final String TAG = InvalidationPreferences.class.getSimpleName(); + + private final Context mContext; + + public InvalidationPreferences(Context context) { + this.mContext = Preconditions.checkNotNull(context.getApplicationContext()); + } + + /** Returns a new {@link EditContext} to modify the preferences managed by this class. */ + public EditContext edit() { + return new EditContext(); + } + + /** + * Applies the changes accumulated in {@code editContext}. Returns whether they were + * successfully written. + * <p> + * NOTE: this method performs blocking I/O and must not be called from the UI thread. + */ + public boolean commit(EditContext editContext) { + if (!editContext.editor.commit()) { + Log.w(TAG, "Failed to commit invalidation preferences"); + return false; + } + return true; + } + + /** Returns the saved sync types, or {@code null} if none exist. */ + @Nullable public Collection<String> getSavedSyncedTypes() { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext); + return preferences.getStringSet(PrefKeys.SYNC_TANGO_TYPES, null); + } + + /** Returns the saved account, or {@code null} if none exists. */ + @Nullable public Account getSavedSyncedAccount() { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext); + String accountName = preferences.getString(PrefKeys.SYNC_ACCT_NAME, null); + String accountType = preferences.getString(PrefKeys.SYNC_ACCT_TYPE, null); + if (accountName == null || accountType == null) { + return null; + } + return new Account(accountName, accountType); + } + + /** Sets the saved sync types to {@code syncTypes} in {@code editContext}. */ + public void setSyncTypes(EditContext editContext, Collection<String> syncTypes) { + Preconditions.checkNotNull(syncTypes); + Set<String> selectedTypesSet = new HashSet<String>(syncTypes); + editContext.editor.putStringSet(PrefKeys.SYNC_TANGO_TYPES, selectedTypesSet); + } + + /** Sets the saved account to {@code account} in {@code editContext}. */ + public void setAccount(EditContext editContext, Account account) { + editContext.editor.putString(PrefKeys.SYNC_ACCT_NAME, account.name); + editContext.editor.putString(PrefKeys.SYNC_ACCT_TYPE, account.type); + } +} diff --git a/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationControllerTest.java b/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationControllerTest.java index eee2bdf..0a84273 100644 --- a/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationControllerTest.java +++ b/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationControllerTest.java @@ -101,7 +101,7 @@ public class InvalidationControllerTest extends InstrumentationTestCase { assertEquals(account, intentAccount); // Validate registered types. - Set<String> expectedTypes = Sets.newHashSet(IntentProtocol.ALL_TYPES_TYPE); + Set<String> expectedTypes = Sets.newHashSet(ModelType.ALL_TYPES_TYPE); Set<String> actualTypes = Sets.newHashSet(); actualTypes.addAll(intent.getStringArrayListExtra(IntentProtocol.EXTRA_REGISTERED_TYPES)); assertEquals(expectedTypes, actualTypes); @@ -113,6 +113,13 @@ public class InvalidationControllerTest extends InstrumentationTestCase { assertEquals(mContext.getPackageName(), mController.getContractAuthority()); } + @SmallTest + @Feature({"Sync"}) + public void testGetIntentDestination() { + assertEquals("org.chromium.sync.notifier.TEST_VALUE", + InvalidationController.getDestinationClassName(mContext)); + } + /** * Asserts that {@code intent} is destined for the correct component. */ diff --git a/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationPreferencesTest.java b/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationPreferencesTest.java new file mode 100644 index 0000000..ddaca6c --- /dev/null +++ b/sync/android/javatests/src/org/chromium/sync/notifier/InvalidationPreferencesTest.java @@ -0,0 +1,99 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.sync.notifier; + +import android.accounts.Account; +import android.content.Context; +import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import com.google.common.collect.Sets; + +import org.chromium.base.test.util.AdvancedMockContext; +import org.chromium.base.test.util.Feature; +import org.chromium.sync.internal_api.pub.base.ModelType; +import org.chromium.sync.notifier.InvalidationPreferences; + +import java.util.HashSet; +import java.util.Set; + +/** + * Tests for the {@link InvalidationPreferences}. + * + * @author dsmyers@google.com (Daniel Myers) + */ +public class InvalidationPreferencesTest extends InstrumentationTestCase { + private Context mContext; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mContext = new AdvancedMockContext(getInstrumentation().getContext()); + } + + @SmallTest + @Feature({"Sync"}) + public void testTranslateBasicSyncTypes() throws Exception { + /* + * Test plan: convert three strings to model types, one of which is invalid. Verify that + * the two valid strings are properly converted and that the invalid string is dropped. + */ + HashSet<ModelType> expectedTypes = Sets.newHashSet(ModelType.BOOKMARK, ModelType.SESSION); + Set<ModelType> actualTypes = ModelType.syncTypesToModelTypes( + Sets.newHashSet("BOOKMARK", "SESSION", "0!!!INVALID")); + assertEquals(expectedTypes, actualTypes); + } + + @SmallTest + @Feature({"Sync"}) + public void testTranslateAllSyncTypes() { + /* + * Test plan: convert the special all-types type to model types. Verify that it is + * properly expanded. + */ + HashSet<ModelType> expectedTypes = Sets.newHashSet(ModelType.values()); + Set<ModelType> actualTypes = ModelType.syncTypesToModelTypes( + Sets.newHashSet(ModelType.ALL_TYPES_TYPE)); + assertEquals(expectedTypes, actualTypes); + } + + @SmallTest + @Feature({"Sync"}) + public void testReadMissingData() { + /* + * Test plan: read saved state from empty preferences. Verify that null is returned. + */ + InvalidationPreferences invPreferences = new InvalidationPreferences(mContext); + assertNull(invPreferences.getSavedSyncedAccount()); + assertNull(invPreferences.getSavedSyncedTypes()); + } + + @SmallTest + @Feature({"Sync"}) + public void testReadWriteAndReadData() { + /* + * Test plan: write and read back saved state. Verify that the returned state is what + * was written. + */ + InvalidationPreferences invPreferences = new InvalidationPreferences(mContext); + InvalidationPreferences.EditContext editContext = invPreferences.edit(); + + // We should never write both a real type and the all-types type in practice, but we test + // with them here to ensure that preferences are not interpreting the written data. + Set<String> syncTypes = Sets.newHashSet("BOOKMARK", ModelType.ALL_TYPES_TYPE); + Account account = new Account("test@example.com", "bogus"); + invPreferences.setSyncTypes(editContext, syncTypes); + invPreferences.setAccount(editContext, account); + + // Nothing should yet have been written. + assertNull(invPreferences.getSavedSyncedAccount()); + assertNull(invPreferences.getSavedSyncedTypes()); + + // Write the new data and verify that they are correctly read back. + invPreferences.commit(editContext); + assertEquals(account, invPreferences.getSavedSyncedAccount()); + assertEquals(syncTypes, invPreferences.getSavedSyncedTypes()); + } +} |