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 True, the XWalkViewInternal created by Crosswalk can be
84 * transformed and animated. Internally, Crosswalk is alternatively using
85 * TextureView as the backend of XWalkViewInternal.
87 * <a href="http://developer.android.com/reference/android/view/TextureView.html">
88 * TextureView</a> is a kind of
89 * <a href="http://developer.android.com/reference/android/view/View.html">
90 * android.view.View</a> that is different from
91 * <a href="http://developer.android.com/reference/android/view/SurfaceView.html">
92 * SurfaceView</a>. Unlike SurfaceView, it can be resized, transformed and
93 * animated. Once this key is set to True, all XWalkViewInternal will use TextureView
94 * as the rendering target instead of SurfaceView. The downside of TextureView
95 * is, it would consume more graphics memory than SurfaceView and may have
96 * 1~3 extra frames of latency to display updates.
98 * Note this key MUST be set before creating the first XWalkViewInternal, otherwise
99 * a RuntimeException will be thrown.
104 public static final String ANIMATABLE_XWALK_VIEW = "animatable-xwalk-view";
107 * The key string to allow/disallow javascript to open
108 * window automatically.
112 public static final String JAVASCRIPT_CAN_OPEN_WINDOW =
113 "javascript-can-open-window";
116 * The key string to allow/disallow having universal access
121 public static final String ALLOW_UNIVERSAL_ACCESS_FROM_FILE =
122 "allow-universal-access-from-file";
125 * The key string to enable/disable multiple windows.
129 public static final String SUPPORT_MULTIPLE_WINDOWS =
130 "support-multiple-windows";
133 * The key string to enable/disable javascript.
134 * TODO(wang16): Remove this after cordova removes its dependency.
136 static final String ENABLE_JAVASCRIPT = "enable-javascript";
139 sPrefMap.put(REMOTE_DEBUGGING, new PreferenceValue(false));
140 sPrefMap.put(ANIMATABLE_XWALK_VIEW, new PreferenceValue(false));
141 sPrefMap.put(ENABLE_JAVASCRIPT, new PreferenceValue(true));
142 sPrefMap.put(JAVASCRIPT_CAN_OPEN_WINDOW, new PreferenceValue(true));
144 ALLOW_UNIVERSAL_ACCESS_FROM_FILE, new PreferenceValue(false));
145 sPrefMap.put(SUPPORT_MULTIPLE_WINDOWS, new PreferenceValue(true));
149 * Set a boolean preference value into Crosswalk. An exception will be thrown if
150 * the key for the preference is not valid.
151 * @param key the string name of the key.
152 * @param enabled true if setting it as enabled.
156 public static synchronized void setValue(String key, boolean enabled) throws RuntimeException {
158 // If the listener list is not empty, we consider the preference is
159 // loaded by Crosswalk and taken effect already.
160 if (key == ANIMATABLE_XWALK_VIEW && !sListeners.isEmpty()) {
161 throw new RuntimeException("Warning: the preference key " + key +
162 " can not be set if the preference is already loaded by Crosswalk");
164 if (sPrefMap.get(key).getBooleanValue() != enabled) {
165 PreferenceValue v = new PreferenceValue(enabled);
166 sPrefMap.put(key, v);
167 onKeyValueChanged(key, v);
172 * Set an integer preference value into Crosswalk. An exception will be thrown if
173 * the key for the preference is not valid.
174 * @param key the string name of the key.
175 * @param value the integer value.
179 public static synchronized void setValue(String key, int value) throws RuntimeException {
181 // If the listener list is not empty, we consider the preference is
182 // loaded by Crosswalk and taken effect already.
183 if (key == ANIMATABLE_XWALK_VIEW && !sListeners.isEmpty()) {
184 throw new RuntimeException("Warning: the preference key " + key +
185 " can not be set if the preference is already loaded by Crosswalk");
187 if (sPrefMap.get(key).getIntegerValue() != value) {
188 PreferenceValue v = new PreferenceValue(value);
189 sPrefMap.put(key, v);
190 onKeyValueChanged(key, v);
195 * Set a string preference value into Crosswalk. An exception will be thrown if
196 * the key for the preference is not valid.
197 * @param key the string name of the key.
198 * @param value the string value.
202 public static synchronized void setValue(String key, String value) throws RuntimeException {
204 // If the listener list is not empty, we consider the preference is
205 // loaded by Crosswalk and taken effect already.
206 if (key == ANIMATABLE_XWALK_VIEW && !sListeners.isEmpty()) {
207 throw new RuntimeException("Warning: the preference key " + key +
208 " can not be set if the preference is already loaded by Crosswalk");
210 if (value != null && !value.equals(sPrefMap.get(key).getStringValue())) {
211 PreferenceValue v = new PreferenceValue(value);
212 sPrefMap.put(key, v);
213 onKeyValueChanged(key, v);
218 * Get a boolean preference value from Crosswalk. An exception will be thrown if
219 * the key for the preference is not valid.
220 * @param key the string name of the key.
221 * @return true if it's enabled.
226 public static synchronized boolean getValue(String key) throws RuntimeException {
228 return sPrefMap.get(key).getBooleanValue();
232 * Get a boolean preference value from Crosswalk. An exception will be thrown if
233 * the key for the preference is not valid.
234 * @param key the string name of the key.
235 * @return true if it's enabled.
239 public static synchronized boolean getBooleanValue(String key) throws RuntimeException {
241 return sPrefMap.get(key).getBooleanValue();
245 * Get a int preference value from Crosswalk. An exception will be thrown if
246 * the key for the preference is not valid.
247 * @param key the string name of the key.
248 * @return the integer value.
252 public static synchronized int getIntegerValue(String key) throws RuntimeException {
254 return sPrefMap.get(key).getIntegerValue();
258 * Get a string preference value from Crosswalk. An exception will be thrown if
259 * the key for the preference is not valid.
260 * @param key the string name of the key.
261 * @return the string value.
265 public static synchronized String getStringValue(String key) throws RuntimeException {
267 return sPrefMap.get(key).getStringValue();
270 static synchronized void load(KeyValueChangeListener listener) {
271 // Load current settings for initialization of a listener implementor.
272 for (Map.Entry<String, PreferenceValue> entry : sPrefMap.entrySet()) {
273 listener.onKeyValueChanged(entry.getKey(), entry.getValue());
276 registerListener(listener);
279 static synchronized void unload(KeyValueChangeListener listener) {
280 unregisterListener(listener);
283 // Listen to value changes.
284 interface KeyValueChangeListener {
285 public void onKeyValueChanged(String key, PreferenceValue value);
288 private static synchronized void registerListener(KeyValueChangeListener listener) {
289 removeEnqueuedReference();
290 WeakReference<KeyValueChangeListener> weakListener =
291 new WeakReference<KeyValueChangeListener>(listener, sRefQueue);
292 sListeners.add(weakListener);
295 private static synchronized void unregisterListener(KeyValueChangeListener listener) {
296 removeEnqueuedReference();
297 for (WeakReference<KeyValueChangeListener> weakListener : sListeners) {
298 if (weakListener.get() == listener) {
299 sListeners.remove(weakListener);
305 private static void onKeyValueChanged(String key, PreferenceValue value) {
306 for (WeakReference<KeyValueChangeListener> weakListener : sListeners) {
307 KeyValueChangeListener listener = weakListener.get();
308 if (listener != null) listener.onKeyValueChanged(key, value);
312 private static void checkKey(String key) throws RuntimeException {
313 removeEnqueuedReference();
314 if (!sPrefMap.containsKey(key)) {
315 throw new RuntimeException("Warning: the preference key " + key +
316 " is not supported by Crosswalk.");
321 * Internal method to keep track of weak references and remove the enqueued
322 * references from listener list by polling the reference queue.
324 @SuppressWarnings("unchecked")
325 private static void removeEnqueuedReference() {
326 WeakReference<KeyValueChangeListener> toRemove;
327 while ((toRemove = (WeakReference<KeyValueChangeListener>) sRefQueue.poll()) != null) {
328 sListeners.remove(toRemove);