1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org
.chromium
.sync
.notifier
;
7 import android
.accounts
.Account
;
8 import android
.content
.Context
;
9 import android
.content
.SharedPreferences
;
10 import android
.preference
.PreferenceManager
;
11 import android
.util
.Base64
;
12 import android
.util
.Log
;
14 import com
.google
.ipc
.invalidation
.external
.client
.types
.ObjectId
;
16 import org
.chromium
.base
.VisibleForTesting
;
18 import java
.util
.Collection
;
19 import java
.util
.HashSet
;
22 import javax
.annotation
.Nullable
;
25 * Class to manage the preferences used by the invalidation client.
27 * This class provides methods to read and write the preferences used by the invalidation client.
29 * To read a preference, call the appropriate {@code get...} method.
31 * To write a preference, first call {@link #edit} to obtain a {@link EditContext}. Then, make
32 * one or more calls to a {@code set...} method, providing the same edit context to each call.
33 * Finally, call {@link #commit(EditContext)} to save the changes to stable storage.
35 * @author dsmyers@google.com (Daniel Myers)
37 public class InvalidationPreferences
{
39 * Wrapper around a {@link android.content.SharedPreferences.Editor} for the preferences.
40 * Used to avoid exposing raw preference objects to users of this class.
42 public class EditContext
{
43 private final SharedPreferences
.Editor mEditor
;
46 mEditor
= PreferenceManager
.getDefaultSharedPreferences(mContext
).edit();
51 public static class PrefKeys
{
53 * Shared preference key to store the invalidation types that we want to register
57 public static final String SYNC_TANGO_TYPES
= "sync_tango_types";
60 * Shared preference key to store tango object ids for additional objects that we want to
64 public static final String TANGO_OBJECT_IDS
= "tango_object_ids";
66 /** Shared preference key to store the name of the account in use. */
68 public static final String SYNC_ACCT_NAME
= "sync_acct_name";
70 /** Shared preference key to store the type of account in use. */
71 static final String SYNC_ACCT_TYPE
= "sync_acct_type";
73 /** Shared preference key to store internal notification client library state. */
74 static final String SYNC_TANGO_INTERNAL_STATE
= "sync_tango_internal_state";
77 private static final String TAG
= "InvalidationPreferences";
79 private final Context mContext
;
81 public InvalidationPreferences(Context context
) {
82 Context appContext
= context
.getApplicationContext();
83 if (appContext
== null) throw new NullPointerException("Unable to get application context");
84 mContext
= appContext
;
87 /** Returns a new {@link EditContext} to modify the preferences managed by this class. */
88 public EditContext
edit() {
89 return new EditContext();
93 * Applies the changes accumulated in {@code editContext}. Returns whether they were
94 * successfully written.
96 * NOTE: this method performs blocking I/O and must not be called from the UI thread.
98 public boolean commit(EditContext editContext
) {
99 if (!editContext
.mEditor
.commit()) {
100 Log
.w(TAG
, "Failed to commit invalidation preferences");
106 /** Returns the saved sync types, or {@code null} if none exist. */
107 @Nullable public Set
<String
> getSavedSyncedTypes() {
108 SharedPreferences preferences
= PreferenceManager
.getDefaultSharedPreferences(mContext
);
109 return preferences
.getStringSet(PrefKeys
.SYNC_TANGO_TYPES
, null);
112 /** Sets the saved sync types to {@code syncTypes} in {@code editContext}. */
113 public void setSyncTypes(EditContext editContext
, Collection
<String
> syncTypes
) {
114 if (syncTypes
== null) throw new NullPointerException("syncTypes is null.");
115 Set
<String
> selectedTypesSet
= new HashSet
<String
>(syncTypes
);
116 editContext
.mEditor
.putStringSet(PrefKeys
.SYNC_TANGO_TYPES
, selectedTypesSet
);
119 /** Returns the saved non-sync object ids, or {@code null} if none exist. */
121 public Set
<ObjectId
> getSavedObjectIds() {
122 SharedPreferences preferences
= PreferenceManager
.getDefaultSharedPreferences(mContext
);
123 Set
<String
> objectIdStrings
= preferences
.getStringSet(PrefKeys
.TANGO_OBJECT_IDS
, null);
124 if (objectIdStrings
== null) {
127 Set
<ObjectId
> objectIds
= new HashSet
<ObjectId
>(objectIdStrings
.size());
128 for (String objectIdString
: objectIdStrings
) {
129 ObjectId objectId
= getObjectId(objectIdString
);
130 if (objectId
!= null) {
131 objectIds
.add(objectId
);
137 /** Sets the saved non-sync object ids */
138 public void setObjectIds(EditContext editContext
, Collection
<ObjectId
> objectIds
) {
139 if (objectIds
== null) throw new NullPointerException("objectIds is null.");
140 Set
<String
> objectIdStrings
= new HashSet
<String
>(objectIds
.size());
141 for (ObjectId objectId
: objectIds
) {
142 objectIdStrings
.add(getObjectIdString(objectId
));
144 editContext
.mEditor
.putStringSet(PrefKeys
.TANGO_OBJECT_IDS
, objectIdStrings
);
147 /** Returns the saved account, or {@code null} if none exists. */
148 @Nullable public Account
getSavedSyncedAccount() {
149 SharedPreferences preferences
= PreferenceManager
.getDefaultSharedPreferences(mContext
);
150 String accountName
= preferences
.getString(PrefKeys
.SYNC_ACCT_NAME
, null);
151 String accountType
= preferences
.getString(PrefKeys
.SYNC_ACCT_TYPE
, null);
152 if (accountName
== null || accountType
== null) {
155 return new Account(accountName
, accountType
);
158 /** Sets the saved account to {@code account} in {@code editContext}. */
159 public void setAccount(EditContext editContext
, Account account
) {
160 editContext
.mEditor
.putString(PrefKeys
.SYNC_ACCT_NAME
, account
.name
);
161 editContext
.mEditor
.putString(PrefKeys
.SYNC_ACCT_TYPE
, account
.type
);
164 /** Returns the notification client internal state. */
165 @Nullable public byte[] getInternalNotificationClientState() {
166 SharedPreferences preferences
= PreferenceManager
.getDefaultSharedPreferences(mContext
);
167 String base64State
= preferences
.getString(PrefKeys
.SYNC_TANGO_INTERNAL_STATE
, null);
168 if (base64State
== null) {
171 return Base64
.decode(base64State
, Base64
.DEFAULT
);
174 /** Sets the notification client internal state to {@code state}. */
175 public void setInternalNotificationClientState(EditContext editContext
, byte[] state
) {
176 editContext
.mEditor
.putString(PrefKeys
.SYNC_TANGO_INTERNAL_STATE
,
177 Base64
.encodeToString(state
, Base64
.DEFAULT
));
180 /** Converts the given object id to a string for storage in preferences. */
181 private String
getObjectIdString(ObjectId objectId
) {
182 return objectId
.getSource() + ":" + new String(objectId
.getName());
186 * Converts the given object id string stored in preferences to an object id.
187 * Returns null if the string does not represent a valid object id.
189 private ObjectId
getObjectId(String objectIdString
) {
190 int separatorPos
= objectIdString
.indexOf(':');
191 // Ensure that the separator is surrounded by at least one character on each side.
192 if (separatorPos
< 1 || separatorPos
== objectIdString
.length() - 1) {
197 objectSource
= Integer
.parseInt(objectIdString
.substring(0, separatorPos
));
198 } catch (NumberFormatException e
) {
201 byte[] objectName
= objectIdString
.substring(separatorPos
+ 1).getBytes();
202 return ObjectId
.newInstance(objectSource
, objectName
);