Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core_internal / src / org / xwalk / core / internal / XWalkPreferencesInternal.java
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.
4
5 package org.xwalk.core.internal;
6
7 import java.lang.ref.ReferenceQueue;
8 import java.lang.ref.WeakReference;
9 import java.util.ArrayList;
10 import java.util.HashMap;
11 import java.util.Map;
12
13 /**
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.
18  */
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;
25
26         int mType;
27         Object mValue;
28
29         PreferenceValue(boolean value) {
30             mType = PREFERENCE_TYPE_BOOLEAN;
31             mValue = value;
32         }
33
34         PreferenceValue(int value) {
35             mType = PREFERENCE_TYPE_INTEGER;
36             mValue = value;
37         }
38
39         PreferenceValue(String value) {
40             mType = PREFERENCE_TYPE_STRING;
41             mValue = value;
42         }
43
44         int getType() {
45             return mType;
46         }
47
48         boolean getBooleanValue() {
49             if (mType != PREFERENCE_TYPE_BOOLEAN) return false;
50             return (Boolean) mValue;
51         }
52
53         int getIntegerValue() {
54             if (mType != PREFERENCE_TYPE_INTEGER) return -1;
55             return (Integer) mValue;
56         }
57
58         String getStringValue() {
59             if (mType != PREFERENCE_TYPE_STRING) return null;
60             return (String) mValue;
61         }
62     }
63
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>();
71
72     /**
73      * The key string to enable/disable remote debugging.
74      * @since 1.0
75      */
76     @XWalkAPI
77     public static final String REMOTE_DEBUGGING = "remote-debugging";
78
79     /**
80      * The key string to enable/disable animatable XWalkViewInternal. Default value is
81      * false.
82      *
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.
86      *
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.
97      *
98      * Note this key MUST be set before creating the first XWalkViewInternal, otherwise
99      * a RuntimeException will be thrown.
100      *
101      * @since 2.0
102      */
103     @XWalkAPI
104     public static final String ANIMATABLE_XWALK_VIEW = "animatable-xwalk-view";
105
106     /**
107      * The key string to allow/disallow javascript to open
108      * window automatically.
109      * @since 3.0
110      */
111     @XWalkAPI
112     public static final String JAVASCRIPT_CAN_OPEN_WINDOW =
113             "javascript-can-open-window";
114
115     /**
116      * The key string to allow/disallow having universal access
117      * from file origin.
118      * @since 3.0
119      */
120     @XWalkAPI
121     public static final String ALLOW_UNIVERSAL_ACCESS_FROM_FILE =
122             "allow-universal-access-from-file";
123
124     /**
125      * The key string to enable/disable multiple windows.
126      * @since 3.0
127      */
128     @XWalkAPI
129     public static final String SUPPORT_MULTIPLE_WINDOWS =
130             "support-multiple-windows";
131
132     /**
133      * The key string to enable/disable javascript.
134      * TODO(wang16): Remove this after cordova removes its dependency.
135      */
136     static final String ENABLE_JAVASCRIPT = "enable-javascript";
137
138     static {
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));
143         sPrefMap.put(
144                 ALLOW_UNIVERSAL_ACCESS_FROM_FILE, new PreferenceValue(false));
145         sPrefMap.put(SUPPORT_MULTIPLE_WINDOWS, new PreferenceValue(true));
146     }
147
148     /**
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.
153      * @since 1.0
154      */
155     @XWalkAPI
156     public static synchronized void setValue(String key, boolean enabled) throws RuntimeException {
157         checkKey(key);
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");
163         }
164         if (sPrefMap.get(key).getBooleanValue() != enabled) {
165             PreferenceValue v = new PreferenceValue(enabled);
166             sPrefMap.put(key, v);
167             onKeyValueChanged(key, v);
168         }
169     }
170
171     /**
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.
176      * @since 3.0
177      */
178     @XWalkAPI
179     public static synchronized void setValue(String key, int value) throws RuntimeException {
180         checkKey(key);
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");
186         }
187         if (sPrefMap.get(key).getIntegerValue() != value) {
188             PreferenceValue v = new PreferenceValue(value);
189             sPrefMap.put(key, v);
190             onKeyValueChanged(key, v);
191         }
192     }
193
194     /**
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.
199      * @since 3.0
200      */
201     @XWalkAPI
202     public static synchronized void setValue(String key, String value) throws RuntimeException {
203         checkKey(key);
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");
209         }
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);
214         }
215     }
216
217     /**
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.
222      * @since 1.0
223      * @deprecated
224      */
225     @XWalkAPI
226     public static synchronized boolean getValue(String key) throws RuntimeException {
227         checkKey(key);
228         return sPrefMap.get(key).getBooleanValue();
229     }
230
231     /**
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.
236      * @since 3.0
237      */
238     @XWalkAPI
239     public static synchronized boolean getBooleanValue(String key) throws RuntimeException {
240         checkKey(key);
241         return sPrefMap.get(key).getBooleanValue();
242     }
243
244     /**
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.
249      * @since 3.0
250      */
251     @XWalkAPI
252     public static synchronized int getIntegerValue(String key) throws RuntimeException {
253         checkKey(key);
254         return sPrefMap.get(key).getIntegerValue();
255     }
256
257     /**
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.
262      * @since 3.0
263      */
264     @XWalkAPI
265     public static synchronized String getStringValue(String key) throws RuntimeException {
266         checkKey(key);
267         return sPrefMap.get(key).getStringValue();
268     }
269
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());
274         }
275
276         registerListener(listener);
277     }
278
279     static synchronized void unload(KeyValueChangeListener listener) {
280         unregisterListener(listener);
281     }
282
283     // Listen to value changes.
284     interface KeyValueChangeListener {
285         public void onKeyValueChanged(String key, PreferenceValue value);
286     }
287
288     private static synchronized void registerListener(KeyValueChangeListener listener) {
289         removeEnqueuedReference();
290         WeakReference<KeyValueChangeListener> weakListener =
291                 new WeakReference<KeyValueChangeListener>(listener, sRefQueue);
292         sListeners.add(weakListener);
293     }
294
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);
300                 break;
301             }
302         }
303     }
304
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);
309         }
310     }
311
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.");
317         }
318     }
319
320     /**
321      * Internal method to keep track of weak references and remove the enqueued
322      * references from listener list by polling the reference queue.
323      */
324     @SuppressWarnings("unchecked")
325     private static void removeEnqueuedReference() {
326         WeakReference<KeyValueChangeListener> toRemove;
327         while ((toRemove = (WeakReference<KeyValueChangeListener>) sRefQueue.poll()) != null) {
328             sListeners.remove(toRemove);
329         }
330     }
331 }