Upstream version 9.38.205.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      * true.
82      *
83      * If this key is set to False, then SurfaceView will be created internally as the
84      * rendering backend.
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.
88      *
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.
99      *
100      * Note this key MUST be set before creating the first XWalkViewInternal, otherwise
101      * a RuntimeException will be thrown.
102      *
103      * @since 2.0
104      */
105     @XWalkAPI
106     public static final String ANIMATABLE_XWALK_VIEW = "animatable-xwalk-view";
107
108     /**
109      * The key string to allow/disallow javascript to open
110      * window automatically.
111      * @since 3.0
112      */
113     @XWalkAPI
114     public static final String JAVASCRIPT_CAN_OPEN_WINDOW =
115             "javascript-can-open-window";
116
117     /**
118      * The key string to allow/disallow having universal access
119      * from file origin.
120      * @since 3.0
121      */
122     @XWalkAPI
123     public static final String ALLOW_UNIVERSAL_ACCESS_FROM_FILE =
124             "allow-universal-access-from-file";
125
126     /**
127      * The key string to enable/disable multiple windows.
128      * @since 3.0
129      */
130     @XWalkAPI
131     public static final String SUPPORT_MULTIPLE_WINDOWS =
132             "support-multiple-windows";
133
134     /**
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.
138      * @since 3.0
139      */
140     @XWalkAPI
141     public static final String PROFILE_NAME = "profile-name";
142
143     /**
144      * The key string to enable/disable javascript.
145      * TODO(wang16): Remove this after cordova removes its dependency.
146      */
147     static final String ENABLE_JAVASCRIPT = "enable-javascript";
148
149     /**
150      * The key string to enable/disable xwalk extensions.
151      *
152      */
153     static final String ENABLE_EXTENSIONS = "enable-extensions";
154
155     static {
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));
160         sPrefMap.put(
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"));
165     }
166
167     /**
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.
172      * @since 1.0
173      */
174     @XWalkAPI
175     public static synchronized void setValue(String key, boolean enabled) throws RuntimeException {
176         checkKey(key);
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");
182         }
183         if (sPrefMap.get(key).getBooleanValue() != enabled) {
184             PreferenceValue v = new PreferenceValue(enabled);
185             sPrefMap.put(key, v);
186             onKeyValueChanged(key, v);
187         }
188     }
189
190     /**
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.
195      * @since 3.0
196      */
197     @XWalkAPI
198     public static synchronized void setValue(String key, int value) throws RuntimeException {
199         checkKey(key);
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");
205         }
206         if (sPrefMap.get(key).getIntegerValue() != value) {
207             PreferenceValue v = new PreferenceValue(value);
208             sPrefMap.put(key, v);
209             onKeyValueChanged(key, v);
210         }
211     }
212
213     /**
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.
218      * @since 3.0
219      */
220     @XWalkAPI
221     public static synchronized void setValue(String key, String value) throws RuntimeException {
222         checkKey(key);
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");
228         }
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);
233         }
234     }
235
236     /**
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.
241      * @since 1.0
242      * @deprecated
243      */
244     @XWalkAPI
245     public static synchronized boolean getValue(String key) throws RuntimeException {
246         checkKey(key);
247         return sPrefMap.get(key).getBooleanValue();
248     }
249
250     /**
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.
255      * @since 3.0
256      */
257     @XWalkAPI
258     public static synchronized boolean getBooleanValue(String key) throws RuntimeException {
259         checkKey(key);
260         return sPrefMap.get(key).getBooleanValue();
261     }
262
263     /**
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.
268      * @since 3.0
269      */
270     @XWalkAPI
271     public static synchronized int getIntegerValue(String key) throws RuntimeException {
272         checkKey(key);
273         return sPrefMap.get(key).getIntegerValue();
274     }
275
276     /**
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.
281      * @since 3.0
282      */
283     @XWalkAPI
284     public static synchronized String getStringValue(String key) throws RuntimeException {
285         checkKey(key);
286         return sPrefMap.get(key).getStringValue();
287     }
288
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());
293         }
294
295         registerListener(listener);
296     }
297
298     static synchronized void unload(KeyValueChangeListener listener) {
299         unregisterListener(listener);
300     }
301
302     // Listen to value changes.
303     interface KeyValueChangeListener {
304         public void onKeyValueChanged(String key, PreferenceValue value);
305     }
306
307     private static synchronized void registerListener(KeyValueChangeListener listener) {
308         removeEnqueuedReference();
309         WeakReference<KeyValueChangeListener> weakListener =
310                 new WeakReference<KeyValueChangeListener>(listener, sRefQueue);
311         sListeners.add(weakListener);
312     }
313
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);
319                 break;
320             }
321         }
322     }
323
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);
328         }
329     }
330
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.");
336         }
337     }
338
339     /**
340      * Internal method to keep track of weak references and remove the enqueued
341      * references from listener list by polling the reference queue.
342      */
343     @SuppressWarnings("unchecked")
344     private static void removeEnqueuedReference() {
345         WeakReference<KeyValueChangeListener> toRemove;
346         while ((toRemove = (WeakReference<KeyValueChangeListener>) sRefQueue.poll()) != null) {
347             sListeners.remove(toRemove);
348         }
349     }
350 }