Upstream version 8.36.169.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 public class XWalkPreferencesInternal {
20     private static HashMap<String, Boolean> sPrefMap = new HashMap<String, Boolean>();
21     // Here we use WeakReference to make sure the KeyValueChangeListener instance
22     // can be GC-ed to avoid memory leaking issue.
23     private static ArrayList<WeakReference<KeyValueChangeListener> > sListeners =
24             new ArrayList<WeakReference<KeyValueChangeListener> >();
25     private static ReferenceQueue<KeyValueChangeListener> sRefQueue =
26             new ReferenceQueue<KeyValueChangeListener>();
27
28     /**
29      * The key string to enable/disable remote debugging.
30      * @since 1.0
31      */
32     public static final String REMOTE_DEBUGGING = "remote-debugging";
33
34     /**
35      * The key string to enable/disable animatable XWalkViewInternal. Default value is
36      * false.
37      *
38      * If this key is set to True, the XWalkViewInternal created by Crosswalk can be
39      * transformed and animated. Internally, Crosswalk is alternatively using
40      * TextureView as the backend of XWalkViewInternal.
41      *
42      * <a href="http://developer.android.com/reference/android/view/TextureView.html">
43      * TextureView</a> is a kind of
44      * <a href="http://developer.android.com/reference/android/view/View.html">
45      * android.view.View</a> that is different from
46      * <a href="http://developer.android.com/reference/android/view/SurfaceView.html">
47      * SurfaceView</a>. Unlike SurfaceView, it can be resized, transformed and
48      * animated. Once this key is set to True, all XWalkViewInternal will use TextureView
49      * as the rendering target instead of SurfaceView. The downside of TextureView
50      * is, it would consume more graphics memory than SurfaceView and may have
51      * 1~3 extra frames of latency to display updates.
52      *
53      * Note this key MUST be set before creating the first XWalkViewInternal, otherwise
54      * a RuntimeException will be thrown.
55      *
56      * @since 2.0
57      */
58     public static final String ANIMATABLE_XWALK_VIEW = "animatable-xwalk-view";
59
60     /**
61      * The key string to enable/disable javascript.
62      */
63     static final String ENABLE_JAVASCRIPT = "enable-javascript";
64
65     /**
66      * The key string to allow/disallow javascript to open
67      * window automatically.
68      */
69     static final String JAVASCRIPT_CAN_OPEN_WINDOW =
70             "javascript-can-open-window";
71
72     /**
73      * The key string to allow/disallow having universal access
74      * from file origin.
75      */
76     static final String ALLOW_UNIVERSAL_ACCESS_FROM_FILE =
77             "allow-universal-access-from-file";
78
79     static {
80         sPrefMap.put(REMOTE_DEBUGGING, Boolean.FALSE);
81         sPrefMap.put(ANIMATABLE_XWALK_VIEW, Boolean.FALSE);
82         sPrefMap.put(ENABLE_JAVASCRIPT, Boolean.TRUE);
83         sPrefMap.put(JAVASCRIPT_CAN_OPEN_WINDOW, Boolean.TRUE);
84         sPrefMap.put(
85                 ALLOW_UNIVERSAL_ACCESS_FROM_FILE, Boolean.FALSE);
86     }
87
88     /**
89      * Set a preference value into Crosswalk. An exception will be thrown if
90      * the key for the preference is not valid.
91      * @param key the string name of the key.
92      * @param enabled true if setting it as enabled.
93      * @since 1.0
94      */
95     public static synchronized void setValue(String key, boolean enabled) throws RuntimeException {
96         checkKey(key);
97         // If the listener list is not empty, we consider the preference is
98         // loaded by Crosswalk and taken effect already.
99         if (key == ANIMATABLE_XWALK_VIEW && !sListeners.isEmpty()) {
100             throw new RuntimeException("Warning: the preference key " + key +
101                     " can not be set if the preference is already loaded by Crosswalk");
102         }
103         if (sPrefMap.get(key) != enabled) {
104             sPrefMap.put(key, new Boolean(enabled));
105             onKeyValueChanged(key, enabled);
106         }
107     }
108
109     /**
110      * Get a preference value from Crosswalk. An exception will be thrown if
111      * the key for the preference is not valid.
112      * @param key the string name of the key.
113      * @return true if it's enabled.
114      * @since 1.0
115      */
116     public static synchronized boolean getValue(String key) throws RuntimeException {
117         checkKey(key);
118         return sPrefMap.get(key);
119     }
120
121     // TODO(yongsheng): I believe this is needed?
122     /*public static synchronized void setValue(String key, int value) throws RuntimeException {
123     }*/
124
125     static synchronized void load(KeyValueChangeListener listener) {
126         // Load current settings for initialization of a listener implementor.
127         for (Map.Entry<String, Boolean> entry : sPrefMap.entrySet()) {
128             listener.onKeyValueChanged(entry.getKey(), entry.getValue());
129         }
130
131         registerListener(listener);
132     }
133
134     static synchronized void unload(KeyValueChangeListener listener) {
135         unregisterListener(listener);
136     }
137
138     // Listen to value changes.
139     interface KeyValueChangeListener {
140         public void onKeyValueChanged(String key, boolean value);
141     }
142
143     private static synchronized void registerListener(KeyValueChangeListener listener) {
144         removeEnqueuedReference();
145         WeakReference<KeyValueChangeListener> weakListener =
146                 new WeakReference<KeyValueChangeListener>(listener, sRefQueue);
147         sListeners.add(weakListener);
148     }
149
150     private static synchronized void unregisterListener(KeyValueChangeListener listener) {
151         removeEnqueuedReference();
152         for (WeakReference<KeyValueChangeListener> weakListener : sListeners) {
153             if (weakListener.get() == listener) {
154                 sListeners.remove(weakListener);
155                 break;
156             }
157         }
158     }
159
160     private static void onKeyValueChanged(String key, boolean enabled) {
161         for (WeakReference<KeyValueChangeListener> weakListener : sListeners) {
162             KeyValueChangeListener listener = weakListener.get();
163             if (listener != null) listener.onKeyValueChanged(key, enabled);
164         }
165     }
166
167     private static void checkKey(String key) throws RuntimeException {
168         removeEnqueuedReference();
169         if (!sPrefMap.containsKey(key)) {
170             throw new RuntimeException("Warning: the preference key " + key +
171                     " is not supported by Crosswalk.");
172         }
173     }
174
175     /**
176      * Internal method to keep track of weak references and remove the enqueued
177      * references from listener list by polling the reference queue.
178      */
179     @SuppressWarnings("unchecked")
180     private static void removeEnqueuedReference() {
181         WeakReference<KeyValueChangeListener> toRemove;
182         while ((toRemove = (WeakReference<KeyValueChangeListener>) sRefQueue.poll()) != null) {
183             sListeners.remove(toRemove);
184         }
185     }
186 }