1 // Copyright (c) 2014 Intel Corporation. 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.xwalk.core.internal;
7 import java.lang.ref.ReferenceQueue;
8 import java.lang.ref.WeakReference;
9 import java.util.ArrayList;
10 import java.util.HashMap;
14 * This class represents the preferences and could be set by callers.
15 * It is not thread-safe and must be called on the UI thread.
16 * Afterwards, the preference could be read from all threads and can impact
17 * all XWalkViewInternal instances.
19 @XWalkAPI(noInstance = true)
20 public class XWalkPreferencesInternal {
21 static class PreferenceValue {
22 static final int PREFERENCE_TYPE_BOOLEAN = 1;
23 static final int PREFERENCE_TYPE_INTEGER = 2;
24 static final int PREFERENCE_TYPE_STRING = 3;
29 PreferenceValue(boolean value) {
30 mType = PREFERENCE_TYPE_BOOLEAN;
34 PreferenceValue(int value) {
35 mType = PREFERENCE_TYPE_INTEGER;
39 PreferenceValue(String value) {
40 mType = PREFERENCE_TYPE_STRING;
48 boolean getBooleanValue() {
49 if (mType != PREFERENCE_TYPE_BOOLEAN) return false;
50 return (Boolean) mValue;
53 int getIntegerValue() {
54 if (mType != PREFERENCE_TYPE_INTEGER) return -1;
55 return (Integer) mValue;
58 String getStringValue() {
59 if (mType != PREFERENCE_TYPE_STRING) return null;
60 return (String) mValue;
64 private static HashMap<String, PreferenceValue> sPrefMap = new HashMap<String, PreferenceValue>();
65 // Here we use WeakReference to make sure the KeyValueChangeListener instance
66 // can be GC-ed to avoid memory leaking issue.
67 private static ArrayList<WeakReference<KeyValueChangeListener> > sListeners =
68 new ArrayList<WeakReference<KeyValueChangeListener> >();
69 private static ReferenceQueue<KeyValueChangeListener> sRefQueue =
70 new ReferenceQueue<KeyValueChangeListener>();
73 * The key string to enable/disable remote debugging.
77 public static final String REMOTE_DEBUGGING = "remote-debugging";
80 * The key string to enable/disable animatable XWalkViewInternal. Default value is
83 * If this key is set to False, then SurfaceView will be created internally as the
85 * If this key is set to True, the XWalkViewInternal created by Crosswalk can be
86 * transformed and animated. Internally, Crosswalk is alternatively using
87 * TextureView as the backend of XWalkViewInternal.
89 * <a href="http://developer.android.com/reference/android/view/TextureView.html">
90 * TextureView</a> is a kind of
91 * <a href="http://developer.android.com/reference/android/view/View.html">
92 * android.view.View</a> that is different from
93 * <a href="http://developer.android.com/reference/android/view/SurfaceView.html">
94 * SurfaceView</a>. Unlike SurfaceView, it can be resized, transformed and
95 * animated. Once this key is set to True, all XWalkViewInternal will use TextureView
96 * as the rendering target instead of SurfaceView. The downside of TextureView
97 * is, it would consume more graphics memory than SurfaceView and may have
98 * 1~3 extra frames of latency to display updates.
100 * Note this key MUST be set before creating the first XWalkViewInternal, otherwise
101 * a RuntimeException will be thrown.
106 public static final String ANIMATABLE_XWALK_VIEW = "animatable-xwalk-view";
109 * The key string to allow/disallow javascript to open
110 * window automatically.
114 public static final String JAVASCRIPT_CAN_OPEN_WINDOW =
115 "javascript-can-open-window";
118 * The key string to allow/disallow having universal access
123 public static final String ALLOW_UNIVERSAL_ACCESS_FROM_FILE =
124 "allow-universal-access-from-file";
127 * The key string to enable/disable multiple windows.
131 public static final String SUPPORT_MULTIPLE_WINDOWS =
132 "support-multiple-windows";
135 * The key string to set xwalk profile name.
136 * User data will be kept separated for different profiles.
137 * Profile needs to be set before any XWalkView instance created.
141 public static final String PROFILE_NAME = "profile-name";
144 * The key string to enable/disable javascript.
145 * TODO(wang16): Remove this after cordova removes its dependency.
147 static final String ENABLE_JAVASCRIPT = "enable-javascript";
150 * The key string to enable/disable xwalk extensions.
153 static final String ENABLE_EXTENSIONS = "enable-extensions";
156 sPrefMap.put(REMOTE_DEBUGGING, new PreferenceValue(false));
157 sPrefMap.put(ANIMATABLE_XWALK_VIEW, new PreferenceValue(true));
158 sPrefMap.put(ENABLE_JAVASCRIPT, new PreferenceValue(true));
159 sPrefMap.put(JAVASCRIPT_CAN_OPEN_WINDOW, new PreferenceValue(true));
161 ALLOW_UNIVERSAL_ACCESS_FROM_FILE, new PreferenceValue(false));
162 sPrefMap.put(SUPPORT_MULTIPLE_WINDOWS, new PreferenceValue(true));
163 sPrefMap.put(ENABLE_EXTENSIONS, new PreferenceValue(true));
164 sPrefMap.put(PROFILE_NAME, new PreferenceValue("Default"));
168 * Set a boolean preference value into Crosswalk. An exception will be thrown if
169 * the key for the preference is not valid.
170 * @param key the string name of the key.
171 * @param enabled true if setting it as enabled.
175 public static synchronized void setValue(String key, boolean enabled) throws RuntimeException {
177 // If the listener list is not empty, we consider the preference is
178 // loaded by Crosswalk and taken effect already.
179 if (key == ANIMATABLE_XWALK_VIEW && !sListeners.isEmpty()) {
180 throw new RuntimeException("Warning: the preference key " + key +
181 " can not be set if the preference is already loaded by Crosswalk");
183 if (sPrefMap.get(key).getBooleanValue() != enabled) {
184 PreferenceValue v = new PreferenceValue(enabled);
185 sPrefMap.put(key, v);
186 onKeyValueChanged(key, v);
191 * Set an integer preference value into Crosswalk. An exception will be thrown if
192 * the key for the preference is not valid.
193 * @param key the string name of the key.
194 * @param value the integer value.
198 public static synchronized void setValue(String key, int value) throws RuntimeException {
200 // If the listener list is not empty, we consider the preference is
201 // loaded by Crosswalk and taken effect already.
202 if (key == ANIMATABLE_XWALK_VIEW && !sListeners.isEmpty()) {
203 throw new RuntimeException("Warning: the preference key " + key +
204 " can not be set if the preference is already loaded by Crosswalk");
206 if (sPrefMap.get(key).getIntegerValue() != value) {
207 PreferenceValue v = new PreferenceValue(value);
208 sPrefMap.put(key, v);
209 onKeyValueChanged(key, v);
214 * Set a string preference value into Crosswalk. An exception will be thrown if
215 * the key for the preference is not valid.
216 * @param key the string name of the key.
217 * @param value the string value.
221 public static synchronized void setValue(String key, String value) throws RuntimeException {
223 // If the listener list is not empty, we consider the preference is
224 // loaded by Crosswalk and taken effect already.
225 if (key == ANIMATABLE_XWALK_VIEW && !sListeners.isEmpty()) {
226 throw new RuntimeException("Warning: the preference key " + key +
227 " can not be set if the preference is already loaded by Crosswalk");
229 if (value != null && !value.equals(sPrefMap.get(key).getStringValue())) {
230 PreferenceValue v = new PreferenceValue(value);
231 sPrefMap.put(key, v);
232 onKeyValueChanged(key, v);
237 * Get a boolean preference value from Crosswalk. An exception will be thrown if
238 * the key for the preference is not valid.
239 * @param key the string name of the key.
240 * @return true if it's enabled.
245 public static synchronized boolean getValue(String key) throws RuntimeException {
247 return sPrefMap.get(key).getBooleanValue();
251 * Get a boolean preference value from Crosswalk. An exception will be thrown if
252 * the key for the preference is not valid.
253 * @param key the string name of the key.
254 * @return true if it's enabled.
258 public static synchronized boolean getBooleanValue(String key) throws RuntimeException {
260 return sPrefMap.get(key).getBooleanValue();
264 * Get a int preference value from Crosswalk. An exception will be thrown if
265 * the key for the preference is not valid.
266 * @param key the string name of the key.
267 * @return the integer value.
271 public static synchronized int getIntegerValue(String key) throws RuntimeException {
273 return sPrefMap.get(key).getIntegerValue();
277 * Get a string preference value from Crosswalk. An exception will be thrown if
278 * the key for the preference is not valid.
279 * @param key the string name of the key.
280 * @return the string value.
284 public static synchronized String getStringValue(String key) throws RuntimeException {
286 return sPrefMap.get(key).getStringValue();
289 static synchronized void load(KeyValueChangeListener listener) {
290 // Load current settings for initialization of a listener implementor.
291 for (Map.Entry<String, PreferenceValue> entry : sPrefMap.entrySet()) {
292 listener.onKeyValueChanged(entry.getKey(), entry.getValue());
295 registerListener(listener);
298 static synchronized void unload(KeyValueChangeListener listener) {
299 unregisterListener(listener);
302 // Listen to value changes.
303 interface KeyValueChangeListener {
304 public void onKeyValueChanged(String key, PreferenceValue value);
307 private static synchronized void registerListener(KeyValueChangeListener listener) {
308 removeEnqueuedReference();
309 WeakReference<KeyValueChangeListener> weakListener =
310 new WeakReference<KeyValueChangeListener>(listener, sRefQueue);
311 sListeners.add(weakListener);
314 private static synchronized void unregisterListener(KeyValueChangeListener listener) {
315 removeEnqueuedReference();
316 for (WeakReference<KeyValueChangeListener> weakListener : sListeners) {
317 if (weakListener.get() == listener) {
318 sListeners.remove(weakListener);
324 private static void onKeyValueChanged(String key, PreferenceValue value) {
325 for (WeakReference<KeyValueChangeListener> weakListener : sListeners) {
326 KeyValueChangeListener listener = weakListener.get();
327 if (listener != null) listener.onKeyValueChanged(key, value);
331 private static void checkKey(String key) throws RuntimeException {
332 removeEnqueuedReference();
333 if (!sPrefMap.containsKey(key)) {
334 throw new RuntimeException("Warning: the preference key " + key +
335 " is not supported by Crosswalk.");
340 * Internal method to keep track of weak references and remove the enqueued
341 * references from listener list by polling the reference queue.
343 @SuppressWarnings("unchecked")
344 private static void removeEnqueuedReference() {
345 WeakReference<KeyValueChangeListener> toRemove;
346 while ((toRemove = (WeakReference<KeyValueChangeListener>) sRefQueue.poll()) != null) {
347 sListeners.remove(toRemove);