OLD | NEW |
| (Empty) |
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. | |
4 | |
5 package org.chromium.sync.notifier; | |
6 | |
7 import android.accounts.Account; | |
8 import android.content.SharedPreferences; | |
9 import android.util.Base64; | |
10 | |
11 import com.google.ipc.invalidation.external.client.types.ObjectId; | |
12 | |
13 import org.chromium.base.ContextUtils; | |
14 import org.chromium.base.Log; | |
15 import org.chromium.base.VisibleForTesting; | |
16 | |
17 import java.util.Collection; | |
18 import java.util.Collections; | |
19 import java.util.HashSet; | |
20 import java.util.Set; | |
21 | |
22 import javax.annotation.Nullable; | |
23 | |
24 /** | |
25 * Class to manage the preferences used by the invalidation client. | |
26 * <p> | |
27 * This class provides methods to read and write the preferences used by the inv
alidation client. | |
28 * <p> | |
29 * To read a preference, call the appropriate {@code get...} method. | |
30 * <p> | |
31 * To write a preference, first call {@link #edit} to obtain a {@link EditContex
t}. 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 stor
age. | |
34 * | |
35 * @author dsmyers@google.com (Daniel Myers) | |
36 */ | |
37 public class InvalidationPreferences { | |
38 /** | |
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. | |
41 */ | |
42 public static class EditContext { | |
43 private final SharedPreferences.Editor mEditor; | |
44 | |
45 EditContext() { | |
46 mEditor = ContextUtils.getAppSharedPreferences().edit(); | |
47 } | |
48 } | |
49 | |
50 /** | |
51 * Internal class to wrap constants for preference keys. | |
52 */ | |
53 @VisibleForTesting | |
54 public static class PrefKeys { | |
55 /** | |
56 * Shared preference key to store the invalidation types that we want to
register | |
57 * for. | |
58 */ | |
59 @VisibleForTesting | |
60 public static final String SYNC_TANGO_TYPES = "sync_tango_types"; | |
61 | |
62 /** | |
63 * Shared preference key to store tango object ids for additional object
s that we want to | |
64 * register for. | |
65 */ | |
66 @VisibleForTesting | |
67 public static final String TANGO_OBJECT_IDS = "tango_object_ids"; | |
68 | |
69 /** Shared preference key to store the name of the account in use. */ | |
70 @VisibleForTesting | |
71 public static final String SYNC_ACCT_NAME = "sync_acct_name"; | |
72 | |
73 /** Shared preference key to store the type of account in use. */ | |
74 static final String SYNC_ACCT_TYPE = "sync_acct_type"; | |
75 | |
76 /** Shared preference key to store internal notification client library
state. */ | |
77 static final String SYNC_TANGO_INTERNAL_STATE = "sync_tango_internal_sta
te"; | |
78 } | |
79 | |
80 private static final String TAG = "InvalidationPreferences"; | |
81 | |
82 // Only one commit call can be in progress at a time. | |
83 private static final Object sCommitLock = new Object(); | |
84 | |
85 /** Returns a new {@link EditContext} to modify the preferences managed by t
his class. */ | |
86 public EditContext edit() { | |
87 return new EditContext(); | |
88 } | |
89 | |
90 /** | |
91 * Applies the changes accumulated in {@code editContext}. Returns whether t
hey were | |
92 * successfully written. | |
93 * <p> | |
94 * NOTE: this method performs blocking I/O and must not be called from the U
I thread. | |
95 */ | |
96 public boolean commit(EditContext editContext) { | |
97 synchronized (sCommitLock) { | |
98 if (!editContext.mEditor.commit()) { | |
99 Log.w(TAG, "Failed to commit invalidation preferences"); | |
100 return false; | |
101 } | |
102 return true; | |
103 } | |
104 } | |
105 | |
106 /** Returns the saved sync types, or {@code null} if none exist. */ | |
107 @Nullable public Set<String> getSavedSyncedTypes() { | |
108 SharedPreferences preferences = ContextUtils.getAppSharedPreferences(); | |
109 Set<String> syncedTypes = preferences.getStringSet(PrefKeys.SYNC_TANGO_T
YPES, null); | |
110 // Wrap with unmodifiableSet to ensure it's never modified. See crbug.co
m/568369. | |
111 return syncedTypes == null ? null : Collections.unmodifiableSet(syncedTy
pes); | |
112 } | |
113 | |
114 /** Sets the saved sync types to {@code syncTypes} in {@code editContext}. *
/ | |
115 public void setSyncTypes(EditContext editContext, Collection<String> syncTyp
es) { | |
116 if (syncTypes == null) throw new NullPointerException("syncTypes is null
."); | |
117 Set<String> selectedTypesSet = new HashSet<String>(syncTypes); | |
118 editContext.mEditor.putStringSet(PrefKeys.SYNC_TANGO_TYPES, selectedType
sSet); | |
119 } | |
120 | |
121 /** Returns the saved non-sync object ids, or {@code null} if none exist. */ | |
122 @Nullable | |
123 public Set<ObjectId> getSavedObjectIds() { | |
124 SharedPreferences preferences = ContextUtils.getAppSharedPreferences(); | |
125 Set<String> objectIdStrings = preferences.getStringSet(PrefKeys.TANGO_OB
JECT_IDS, null); | |
126 if (objectIdStrings == null) { | |
127 return null; | |
128 } | |
129 Set<ObjectId> objectIds = new HashSet<ObjectId>(objectIdStrings.size()); | |
130 for (String objectIdString : objectIdStrings) { | |
131 ObjectId objectId = getObjectId(objectIdString); | |
132 if (objectId != null) { | |
133 objectIds.add(objectId); | |
134 } | |
135 } | |
136 return objectIds; | |
137 } | |
138 | |
139 /** Sets the saved non-sync object ids */ | |
140 public void setObjectIds(EditContext editContext, Collection<ObjectId> objec
tIds) { | |
141 if (objectIds == null) throw new NullPointerException("objectIds is null
."); | |
142 Set<String> objectIdStrings = new HashSet<String>(objectIds.size()); | |
143 for (ObjectId objectId : objectIds) { | |
144 objectIdStrings.add(getObjectIdString(objectId)); | |
145 } | |
146 editContext.mEditor.putStringSet(PrefKeys.TANGO_OBJECT_IDS, objectIdStri
ngs); | |
147 } | |
148 | |
149 /** Returns the saved account, or {@code null} if none exists. */ | |
150 @Nullable public Account getSavedSyncedAccount() { | |
151 SharedPreferences preferences = ContextUtils.getAppSharedPreferences(); | |
152 String accountName = preferences.getString(PrefKeys.SYNC_ACCT_NAME, null
); | |
153 String accountType = preferences.getString(PrefKeys.SYNC_ACCT_TYPE, null
); | |
154 if (accountName == null || accountType == null) { | |
155 return null; | |
156 } | |
157 return new Account(accountName, accountType); | |
158 } | |
159 | |
160 /** Sets the saved account to {@code account} in {@code editContext}. */ | |
161 public void setAccount(EditContext editContext, Account account) { | |
162 editContext.mEditor.putString(PrefKeys.SYNC_ACCT_NAME, account.name); | |
163 editContext.mEditor.putString(PrefKeys.SYNC_ACCT_TYPE, account.type); | |
164 } | |
165 | |
166 /** Returns the notification client internal state. */ | |
167 @Nullable public byte[] getInternalNotificationClientState() { | |
168 SharedPreferences preferences = ContextUtils.getAppSharedPreferences(); | |
169 String base64State = preferences.getString(PrefKeys.SYNC_TANGO_INTERNAL_
STATE, null); | |
170 if (base64State == null) { | |
171 return null; | |
172 } | |
173 return Base64.decode(base64State, Base64.DEFAULT); | |
174 } | |
175 | |
176 /** Sets the notification client internal state to {@code state}. */ | |
177 public void setInternalNotificationClientState(EditContext editContext, byte
[] state) { | |
178 editContext.mEditor.putString(PrefKeys.SYNC_TANGO_INTERNAL_STATE, | |
179 Base64.encodeToString(state, Base64.DEFAULT)); | |
180 } | |
181 | |
182 /** Converts the given object id to a string for storage in preferences. */ | |
183 private String getObjectIdString(ObjectId objectId) { | |
184 return objectId.getSource() + ":" + new String(objectId.getName()); | |
185 } | |
186 | |
187 /** | |
188 * Converts the given object id string stored in preferences to an object id
. | |
189 * Returns null if the string does not represent a valid object id. | |
190 */ | |
191 private ObjectId getObjectId(String objectIdString) { | |
192 int separatorPos = objectIdString.indexOf(':'); | |
193 // Ensure that the separator is surrounded by at least one character on
each side. | |
194 if (separatorPos < 1 || separatorPos == objectIdString.length() - 1) { | |
195 return null; | |
196 } | |
197 int objectSource; | |
198 try { | |
199 objectSource = Integer.parseInt(objectIdString.substring(0, separato
rPos)); | |
200 } catch (NumberFormatException e) { | |
201 return null; | |
202 } | |
203 byte[] objectName = objectIdString.substring(separatorPos + 1).getBytes(
); | |
204 return ObjectId.newInstance(objectSource, objectName); | |
205 } | |
206 } | |
OLD | NEW |