Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 package org.mozilla.gecko;
8 import org.mozilla.gecko.EventDispatcher;
9 import org.mozilla.gecko.util.GeckoEventListener;
11 import org.json.JSONArray;
12 import org.json.JSONException;
13 import org.json.JSONObject;
15 import android.content.Context;
16 import android.content.SharedPreferences;
17 import android.util.Log;
19 import java.util.Map;
20 import java.util.HashMap;
22 /**
23 * Helper class to get, set, and observe Android Shared Preferences.
24 */
25 public final class SharedPreferencesHelper
26 implements GeckoEventListener
27 {
28 public static final String LOGTAG = "GeckoAndSharedPrefs";
30 protected final Context mContext;
32 // mListeners is not synchronized because it is only updated in
33 // handleObserve, which is called from Gecko serially.
34 protected final Map<String, SharedPreferences.OnSharedPreferenceChangeListener> mListeners;
36 public SharedPreferencesHelper(Context context) {
37 mContext = context;
39 mListeners = new HashMap<String, SharedPreferences.OnSharedPreferenceChangeListener>();
41 EventDispatcher dispatcher = GeckoAppShell.getEventDispatcher();
42 if (dispatcher == null) {
43 Log.e(LOGTAG, "Gecko event dispatcher must not be null", new RuntimeException());
44 return;
45 }
46 dispatcher.registerEventListener("SharedPreferences:Set", this);
47 dispatcher.registerEventListener("SharedPreferences:Get", this);
48 dispatcher.registerEventListener("SharedPreferences:Observe", this);
49 }
51 public synchronized void uninit() {
52 EventDispatcher dispatcher = GeckoAppShell.getEventDispatcher();
53 if (dispatcher == null) {
54 Log.e(LOGTAG, "Gecko event dispatcher must not be null", new RuntimeException());
55 return;
56 }
58 dispatcher.unregisterEventListener("SharedPreferences:Set", this);
59 dispatcher.unregisterEventListener("SharedPreferences:Get", this);
60 dispatcher.unregisterEventListener("SharedPreferences:Observe", this);
61 }
63 private SharedPreferences getSharedPreferences(String branch) {
64 if (branch == null) {
65 return GeckoSharedPrefs.forApp(mContext);
66 } else {
67 return mContext.getSharedPreferences(branch, Context.MODE_PRIVATE);
68 }
69 }
71 /**
72 * Set many SharedPreferences in Android.
73 *
74 * message.branch must exist, and should be a String SharedPreferences
75 * branch name, or null for the default branch.
76 * message.preferences should be an array of preferences. Each preference
77 * must include a String name, a String type in ["bool", "int", "string"],
78 * and an Object value.
79 */
80 private void handleSet(JSONObject message) throws JSONException {
81 if (!message.has("branch")) {
82 Log.e(LOGTAG, "No branch specified for SharedPreference:Set; aborting.");
83 return;
84 }
86 String branch = message.isNull("branch") ? null : message.getString("branch");
87 SharedPreferences.Editor editor = getSharedPreferences(branch).edit();
89 JSONArray jsonPrefs = message.getJSONArray("preferences");
91 for (int i = 0; i < jsonPrefs.length(); i++) {
92 JSONObject pref = jsonPrefs.getJSONObject(i);
93 String name = pref.getString("name");
94 String type = pref.getString("type");
95 if ("bool".equals(type)) {
96 editor.putBoolean(name, pref.getBoolean("value"));
97 } else if ("int".equals(type)) {
98 editor.putInt(name, pref.getInt("value"));
99 } else if ("string".equals(type)) {
100 editor.putString(name, pref.getString("value"));
101 } else {
102 Log.w(LOGTAG, "Unknown pref value type [" + type + "] for pref [" + name + "]");
103 }
104 editor.commit();
105 }
106 }
108 /**
109 * Get many SharedPreferences from Android.
110 *
111 * message.branch must exist, and should be a String SharedPreferences
112 * branch name, or null for the default branch.
113 * message.preferences should be an array of preferences. Each preference
114 * must include a String name, and a String type in ["bool", "int",
115 * "string"].
116 */
117 private JSONArray handleGet(JSONObject message) throws JSONException {
118 if (!message.has("branch")) {
119 Log.e(LOGTAG, "No branch specified for SharedPreference:Get; aborting.");
120 return null;
121 }
123 String branch = message.isNull("branch") ? null : message.getString("branch");
124 SharedPreferences prefs = getSharedPreferences(branch);
125 JSONArray jsonPrefs = message.getJSONArray("preferences");
126 JSONArray jsonValues = new JSONArray();
128 for (int i = 0; i < jsonPrefs.length(); i++) {
129 JSONObject pref = jsonPrefs.getJSONObject(i);
130 String name = pref.getString("name");
131 String type = pref.getString("type");
132 JSONObject jsonValue = new JSONObject();
133 jsonValue.put("name", name);
134 jsonValue.put("type", type);
135 try {
136 if ("bool".equals(type)) {
137 boolean value = prefs.getBoolean(name, false);
138 jsonValue.put("value", value);
139 } else if ("int".equals(type)) {
140 int value = prefs.getInt(name, 0);
141 jsonValue.put("value", value);
142 } else if ("string".equals(type)) {
143 String value = prefs.getString(name, "");
144 jsonValue.put("value", value);
145 } else {
146 Log.w(LOGTAG, "Unknown pref value type [" + type + "] for pref [" + name + "]");
147 }
148 } catch (ClassCastException e) {
149 // Thrown if there is a preference with the given name that is
150 // not the right type.
151 Log.w(LOGTAG, "Wrong pref value type [" + type + "] for pref [" + name + "]");
152 }
153 jsonValues.put(jsonValue);
154 }
156 return jsonValues;
157 }
159 private static class ChangeListener
160 implements SharedPreferences.OnSharedPreferenceChangeListener {
161 public final String branch;
163 public ChangeListener(final String branch) {
164 this.branch = branch;
165 }
167 @Override
168 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
169 if (Log.isLoggable(LOGTAG, Log.VERBOSE)) {
170 Log.v(LOGTAG, "Got onSharedPreferenceChanged");
171 }
172 try {
173 final JSONObject msg = new JSONObject();
174 msg.put("branch", this.branch);
175 msg.put("key", key);
177 // Truly, this is awful, but the API impedence is strong: there
178 // is no way to get a single untyped value from a
179 // SharedPreferences instance.
180 msg.put("value", sharedPreferences.getAll().get(key));
182 GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SharedPreferences:Changed", msg.toString()));
183 } catch (JSONException e) {
184 Log.e(LOGTAG, "Got exception creating JSON object", e);
185 return;
186 }
187 }
188 }
190 /**
191 * Register or unregister a SharedPreferences.OnSharedPreferenceChangeListener.
192 *
193 * message.branch must exist, and should be a String SharedPreferences
194 * branch name, or null for the default branch.
195 * message.enable should be a boolean: true to enable listening, false to
196 * disable listening.
197 */
198 private void handleObserve(JSONObject message) throws JSONException {
199 if (!message.has("branch")) {
200 Log.e(LOGTAG, "No branch specified for SharedPreference:Observe; aborting.");
201 return;
202 }
204 String branch = message.isNull("branch") ? null : message.getString("branch");
205 SharedPreferences prefs = getSharedPreferences(branch);
206 boolean enable = message.getBoolean("enable");
208 // mListeners is only modified in this one observer, which is called
209 // from Gecko serially.
210 if (enable && !this.mListeners.containsKey(branch)) {
211 SharedPreferences.OnSharedPreferenceChangeListener listener = new ChangeListener(branch);
212 this.mListeners.put(branch, listener);
213 prefs.registerOnSharedPreferenceChangeListener(listener);
214 }
215 if (!enable && this.mListeners.containsKey(branch)) {
216 SharedPreferences.OnSharedPreferenceChangeListener listener = this.mListeners.remove(branch);
217 prefs.unregisterOnSharedPreferenceChangeListener(listener);
218 }
219 }
221 @Override
222 public void handleMessage(String event, JSONObject message) {
223 // Everything here is synchronous and serial, so we need not worry about
224 // overwriting an in-progress response.
225 try {
226 if (event.equals("SharedPreferences:Set")) {
227 if (Log.isLoggable(LOGTAG, Log.VERBOSE)) {
228 Log.v(LOGTAG, "Got SharedPreferences:Set message.");
229 }
230 handleSet(message);
231 } else if (event.equals("SharedPreferences:Get")) {
232 if (Log.isLoggable(LOGTAG, Log.VERBOSE)) {
233 Log.v(LOGTAG, "Got SharedPreferences:Get message.");
234 }
235 JSONObject obj = new JSONObject();
236 obj.put("values", handleGet(message));
237 EventDispatcher.sendResponse(message, obj);
238 } else if (event.equals("SharedPreferences:Observe")) {
239 if (Log.isLoggable(LOGTAG, Log.VERBOSE)) {
240 Log.v(LOGTAG, "Got SharedPreferences:Observe message.");
241 }
242 handleObserve(message);
243 } else {
244 Log.e(LOGTAG, "SharedPreferencesHelper got unexpected message " + event);
245 return;
246 }
247 } catch (JSONException e) {
248 Log.e(LOGTAG, "Got exception in handleMessage handling event " + event, e);
249 return;
250 }
251 }
252 }