michael@0: // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: this.EXPORTED_SYMBOLS = ["SharedPreferences"]; michael@0: michael@0: const { classes: Cc, interfaces: Ci, utils: Cu } = Components; michael@0: michael@0: // For adding observers. michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/Messaging.jsm"); michael@0: michael@0: /** michael@0: * Create an interface to an Android SharedPreferences branch. michael@0: * michael@0: * branch {String} should be a string describing a preferences branch, michael@0: * like "UpdateService" or "background.data", or null to access the michael@0: * default preferences branch for the application. michael@0: */ michael@0: function SharedPreferences(branch) { michael@0: if (!(this instanceof SharedPreferences)) { michael@0: return new SharedPreferences(branch); michael@0: } michael@0: this._branch = branch || null; michael@0: this._observers = {}; michael@0: }; michael@0: michael@0: SharedPreferences.prototype = Object.freeze({ michael@0: _set: function _set(prefs) { michael@0: sendMessageToJava({ michael@0: type: "SharedPreferences:Set", michael@0: preferences: prefs, michael@0: branch: this._branch, michael@0: }); michael@0: }, michael@0: michael@0: _setOne: function _setOne(prefName, value, type) { michael@0: let prefs = []; michael@0: prefs.push({ michael@0: name: prefName, michael@0: value: value, michael@0: type: type, michael@0: }); michael@0: this._set(prefs); michael@0: }, michael@0: michael@0: setBoolPref: function setBoolPref(prefName, value) { michael@0: this._setOne(prefName, value, "bool"); michael@0: }, michael@0: michael@0: setCharPref: function setCharPref(prefName, value) { michael@0: this._setOne(prefName, value, "string"); michael@0: }, michael@0: michael@0: setIntPref: function setIntPref(prefName, value) { michael@0: this._setOne(prefName, value, "int"); michael@0: }, michael@0: michael@0: _get: function _get(prefs, callback) { michael@0: let result = null; michael@0: sendMessageToJava({ michael@0: type: "SharedPreferences:Get", michael@0: preferences: prefs, michael@0: branch: this._branch, michael@0: }, (data) => { michael@0: result = data.values; michael@0: }); michael@0: michael@0: let thread = Services.tm.currentThread; michael@0: while (result == null) michael@0: thread.processNextEvent(true); michael@0: michael@0: return result; michael@0: }, michael@0: michael@0: _getOne: function _getOne(prefName, type) { michael@0: let prefs = []; michael@0: prefs.push({ michael@0: name: prefName, michael@0: type: type, michael@0: }); michael@0: let values = this._get(prefs); michael@0: if (values.length != 1) { michael@0: throw new Error("Got too many values: " + values.length); michael@0: } michael@0: return values[0].value; michael@0: }, michael@0: michael@0: getBoolPref: function getBoolPref(prefName) { michael@0: return this._getOne(prefName, "bool"); michael@0: }, michael@0: michael@0: getCharPref: function getCharPref(prefName) { michael@0: return this._getOne(prefName, "string"); michael@0: }, michael@0: michael@0: getIntPref: function getIntPref(prefName) { michael@0: return this._getOne(prefName, "int"); michael@0: }, michael@0: michael@0: /** michael@0: * Invoke `observer` after a change to the preference `domain` in michael@0: * the current branch. michael@0: * michael@0: * `observer` should implement the nsIObserver.observe interface. michael@0: */ michael@0: addObserver: function addObserver(domain, observer, holdWeak) { michael@0: if (!domain) michael@0: throw new Error("domain must not be null"); michael@0: if (!observer) michael@0: throw new Error("observer must not be null"); michael@0: if (holdWeak) michael@0: throw new Error("Weak references not yet implemented."); michael@0: michael@0: if (!this._observers.hasOwnProperty(domain)) michael@0: this._observers[domain] = []; michael@0: if (this._observers[domain].indexOf(observer) > -1) michael@0: return; michael@0: michael@0: this._observers[domain].push(observer); michael@0: michael@0: this._updateAndroidListener(); michael@0: }, michael@0: michael@0: /** michael@0: * Do not invoke `observer` after a change to the preference michael@0: * `domain` in the current branch. michael@0: */ michael@0: removeObserver: function removeObserver(domain, observer) { michael@0: if (!this._observers.hasOwnProperty(domain)) michael@0: return; michael@0: let index = this._observers[domain].indexOf(observer); michael@0: if (index < 0) michael@0: return; michael@0: michael@0: this._observers[domain].splice(index, 1); michael@0: if (this._observers[domain].length < 1) michael@0: delete this._observers[domain]; michael@0: michael@0: this._updateAndroidListener(); michael@0: }, michael@0: michael@0: _updateAndroidListener: function _updateAndroidListener() { michael@0: if (this._listening && Object.keys(this._observers).length < 1) michael@0: this._uninstallAndroidListener(); michael@0: if (!this._listening && Object.keys(this._observers).length > 0) michael@0: this._installAndroidListener(); michael@0: }, michael@0: michael@0: _installAndroidListener: function _installAndroidListener() { michael@0: if (this._listening) michael@0: return; michael@0: this._listening = true; michael@0: michael@0: Services.obs.addObserver(this, "SharedPreferences:Changed", false); michael@0: sendMessageToJava({ michael@0: type: "SharedPreferences:Observe", michael@0: enable: true, michael@0: branch: this._branch, michael@0: }); michael@0: }, michael@0: michael@0: observe: function observe(subject, topic, data) { michael@0: if (topic != "SharedPreferences:Changed") { michael@0: return; michael@0: } michael@0: michael@0: let msg = JSON.parse(data); michael@0: if (msg.branch != this._branch) { michael@0: return; michael@0: } michael@0: michael@0: if (!this._observers.hasOwnProperty(msg.key)) { michael@0: return; michael@0: } michael@0: michael@0: let observers = this._observers[msg.key]; michael@0: for (let obs of observers) { michael@0: obs.observe(obs, msg.key, msg.value); michael@0: } michael@0: }, michael@0: michael@0: _uninstallAndroidListener: function _uninstallAndroidListener() { michael@0: if (!this._listening) michael@0: return; michael@0: this._listening = false; michael@0: michael@0: Services.obs.removeObserver(this, "SharedPreferences:Changed"); michael@0: sendMessageToJava({ michael@0: type: "SharedPreferences:Observe", michael@0: enable: false, michael@0: branch: this._branch, michael@0: }); michael@0: }, michael@0: });