|
1 // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- |
|
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 file, |
|
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 "use strict"; |
|
7 |
|
8 this.EXPORTED_SYMBOLS = ["SharedPreferences"]; |
|
9 |
|
10 const { classes: Cc, interfaces: Ci, utils: Cu } = Components; |
|
11 |
|
12 // For adding observers. |
|
13 Cu.import("resource://gre/modules/Services.jsm"); |
|
14 Cu.import("resource://gre/modules/Messaging.jsm"); |
|
15 |
|
16 /** |
|
17 * Create an interface to an Android SharedPreferences branch. |
|
18 * |
|
19 * branch {String} should be a string describing a preferences branch, |
|
20 * like "UpdateService" or "background.data", or null to access the |
|
21 * default preferences branch for the application. |
|
22 */ |
|
23 function SharedPreferences(branch) { |
|
24 if (!(this instanceof SharedPreferences)) { |
|
25 return new SharedPreferences(branch); |
|
26 } |
|
27 this._branch = branch || null; |
|
28 this._observers = {}; |
|
29 }; |
|
30 |
|
31 SharedPreferences.prototype = Object.freeze({ |
|
32 _set: function _set(prefs) { |
|
33 sendMessageToJava({ |
|
34 type: "SharedPreferences:Set", |
|
35 preferences: prefs, |
|
36 branch: this._branch, |
|
37 }); |
|
38 }, |
|
39 |
|
40 _setOne: function _setOne(prefName, value, type) { |
|
41 let prefs = []; |
|
42 prefs.push({ |
|
43 name: prefName, |
|
44 value: value, |
|
45 type: type, |
|
46 }); |
|
47 this._set(prefs); |
|
48 }, |
|
49 |
|
50 setBoolPref: function setBoolPref(prefName, value) { |
|
51 this._setOne(prefName, value, "bool"); |
|
52 }, |
|
53 |
|
54 setCharPref: function setCharPref(prefName, value) { |
|
55 this._setOne(prefName, value, "string"); |
|
56 }, |
|
57 |
|
58 setIntPref: function setIntPref(prefName, value) { |
|
59 this._setOne(prefName, value, "int"); |
|
60 }, |
|
61 |
|
62 _get: function _get(prefs, callback) { |
|
63 let result = null; |
|
64 sendMessageToJava({ |
|
65 type: "SharedPreferences:Get", |
|
66 preferences: prefs, |
|
67 branch: this._branch, |
|
68 }, (data) => { |
|
69 result = data.values; |
|
70 }); |
|
71 |
|
72 let thread = Services.tm.currentThread; |
|
73 while (result == null) |
|
74 thread.processNextEvent(true); |
|
75 |
|
76 return result; |
|
77 }, |
|
78 |
|
79 _getOne: function _getOne(prefName, type) { |
|
80 let prefs = []; |
|
81 prefs.push({ |
|
82 name: prefName, |
|
83 type: type, |
|
84 }); |
|
85 let values = this._get(prefs); |
|
86 if (values.length != 1) { |
|
87 throw new Error("Got too many values: " + values.length); |
|
88 } |
|
89 return values[0].value; |
|
90 }, |
|
91 |
|
92 getBoolPref: function getBoolPref(prefName) { |
|
93 return this._getOne(prefName, "bool"); |
|
94 }, |
|
95 |
|
96 getCharPref: function getCharPref(prefName) { |
|
97 return this._getOne(prefName, "string"); |
|
98 }, |
|
99 |
|
100 getIntPref: function getIntPref(prefName) { |
|
101 return this._getOne(prefName, "int"); |
|
102 }, |
|
103 |
|
104 /** |
|
105 * Invoke `observer` after a change to the preference `domain` in |
|
106 * the current branch. |
|
107 * |
|
108 * `observer` should implement the nsIObserver.observe interface. |
|
109 */ |
|
110 addObserver: function addObserver(domain, observer, holdWeak) { |
|
111 if (!domain) |
|
112 throw new Error("domain must not be null"); |
|
113 if (!observer) |
|
114 throw new Error("observer must not be null"); |
|
115 if (holdWeak) |
|
116 throw new Error("Weak references not yet implemented."); |
|
117 |
|
118 if (!this._observers.hasOwnProperty(domain)) |
|
119 this._observers[domain] = []; |
|
120 if (this._observers[domain].indexOf(observer) > -1) |
|
121 return; |
|
122 |
|
123 this._observers[domain].push(observer); |
|
124 |
|
125 this._updateAndroidListener(); |
|
126 }, |
|
127 |
|
128 /** |
|
129 * Do not invoke `observer` after a change to the preference |
|
130 * `domain` in the current branch. |
|
131 */ |
|
132 removeObserver: function removeObserver(domain, observer) { |
|
133 if (!this._observers.hasOwnProperty(domain)) |
|
134 return; |
|
135 let index = this._observers[domain].indexOf(observer); |
|
136 if (index < 0) |
|
137 return; |
|
138 |
|
139 this._observers[domain].splice(index, 1); |
|
140 if (this._observers[domain].length < 1) |
|
141 delete this._observers[domain]; |
|
142 |
|
143 this._updateAndroidListener(); |
|
144 }, |
|
145 |
|
146 _updateAndroidListener: function _updateAndroidListener() { |
|
147 if (this._listening && Object.keys(this._observers).length < 1) |
|
148 this._uninstallAndroidListener(); |
|
149 if (!this._listening && Object.keys(this._observers).length > 0) |
|
150 this._installAndroidListener(); |
|
151 }, |
|
152 |
|
153 _installAndroidListener: function _installAndroidListener() { |
|
154 if (this._listening) |
|
155 return; |
|
156 this._listening = true; |
|
157 |
|
158 Services.obs.addObserver(this, "SharedPreferences:Changed", false); |
|
159 sendMessageToJava({ |
|
160 type: "SharedPreferences:Observe", |
|
161 enable: true, |
|
162 branch: this._branch, |
|
163 }); |
|
164 }, |
|
165 |
|
166 observe: function observe(subject, topic, data) { |
|
167 if (topic != "SharedPreferences:Changed") { |
|
168 return; |
|
169 } |
|
170 |
|
171 let msg = JSON.parse(data); |
|
172 if (msg.branch != this._branch) { |
|
173 return; |
|
174 } |
|
175 |
|
176 if (!this._observers.hasOwnProperty(msg.key)) { |
|
177 return; |
|
178 } |
|
179 |
|
180 let observers = this._observers[msg.key]; |
|
181 for (let obs of observers) { |
|
182 obs.observe(obs, msg.key, msg.value); |
|
183 } |
|
184 }, |
|
185 |
|
186 _uninstallAndroidListener: function _uninstallAndroidListener() { |
|
187 if (!this._listening) |
|
188 return; |
|
189 this._listening = false; |
|
190 |
|
191 Services.obs.removeObserver(this, "SharedPreferences:Changed"); |
|
192 sendMessageToJava({ |
|
193 type: "SharedPreferences:Observe", |
|
194 enable: false, |
|
195 branch: this._branch, |
|
196 }); |
|
197 }, |
|
198 }); |