Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: mobile/android/thirdparty/org/adblockplus/browser/AddOnBridge.java

Issue 29350065: Issue 2853 - Settings changes are sometimes not saved if the user quits the app (Closed)
Patch Set: Adjusting spacing and also adding code change comment Created Nov. 2, 2016, 11:28 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2015 Eyeo GmbH 3 * Copyright (C) 2006-2015 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 package org.adblockplus.browser; 18 package org.adblockplus.browser;
19 19
20 import java.util.ArrayList;
20 import java.util.HashMap; 21 import java.util.HashMap;
22 import java.util.List;
21 import java.util.Map; 23 import java.util.Map;
24 import java.util.concurrent.Semaphore;
22 25
23 import android.annotation.SuppressLint; 26 import android.annotation.SuppressLint;
27 import android.content.Context;
28 import android.content.SharedPreferences;
24 import android.os.Handler; 29 import android.os.Handler;
25 import android.os.HandlerThread; 30 import android.os.HandlerThread;
26 import android.util.Log; 31 import android.util.Log;
27 32
33 import org.json.JSONArray;
28 import org.json.JSONException; 34 import org.json.JSONException;
29 import org.json.JSONObject; 35 import org.json.JSONObject;
30 import org.mozilla.gecko.GeckoAppShell; 36 import org.mozilla.gecko.GeckoAppShell;
31 import org.mozilla.gecko.util.GeckoRequest; 37 import org.mozilla.gecko.util.GeckoRequest;
32 import org.mozilla.gecko.util.NativeJSObject; 38 import org.mozilla.gecko.util.NativeJSObject;
33 39
34 @SuppressLint("DefaultLocale") 40 @SuppressLint("DefaultLocale")
35 public class AddOnBridge 41 public class AddOnBridge
36 { 42 {
37 private static final String TAG = "AdblockBrowser.AddOnBridge"; 43 private static final String TAG = "AdblockBrowser.AddOnBridge";
38 private static final String REQUEST_NAME = "AdblockPlus:Api"; 44 private static final String REQUEST_NAME = "AdblockPlus:Api";
39 // Timeout for checking filter loading (in seconds) 45 // Timeout for checking filter loading (in seconds)
40 private static final int QUERY_GET_FILTERS_LOADED_TIMEOUT = 30; 46 private static final int QUERY_GET_FILTERS_LOADED_TIMEOUT = 30;
41 // How long to wait between retries (in milliseconds) 47 // How long to wait between retries (in milliseconds)
42 private static final int QUERY_GET_FILTERS_LOADED_DELAY = 500; 48 private static final int QUERY_GET_FILTERS_LOADED_DELAY = 500;
43 // Handler+HandlerThread for posting delayed re-tries without interfering with 49 // Handler+HandlerThread for posting delayed re-tries without interfering with
44 // other threads (e.g. the UI or Gecko thread) 50 // other threads (e.g. the UI or Gecko thread)
45 private static final HandlerThread PRIVATE_HANDLER_THREAD; 51 private static final HandlerThread PRIVATE_HANDLER_THREAD;
46 private static final Handler PRIVATE_HANDLER; 52 private static final Handler PRIVATE_HANDLER;
47 // Global handler, for e.g. UI tasks 53 // Global handler, for e.g. UI tasks
48 private static final HandlerThread GLOBAL_HANDLER_THREAD; 54 private static final HandlerThread GLOBAL_HANDLER_THREAD;
49 private static final Handler GLOBAL_HANDLER; 55 private static final Handler GLOBAL_HANDLER;
56 // Static members for syncing requests that might not have been saved by filte r storage
57 // See https://issues.adblockplus.org/ticket/2853
58 private static final Semaphore SYNC_REQUESTS_SEMAPHORE = new Semaphore(1);
59 private static final int SYNC_REQUESTS_DELAY = 30 * 1000;
60 private static final List<ChainedRequest> PENDING_SYNC_REQUESTS = new ArrayLis t<>();
Felix Dahlke 2016/12/13 11:25:10 I'm a bit confused as to what the point of all thi
61 private static final String PENDING_SYNC_REQUESTS_KEY = "PENDING_SYNC_REQUESTS _KEY";
62
63 private static SharedPreferences sharedPrefs;
50 64
51 static 65 static
52 { 66 {
53 PRIVATE_HANDLER_THREAD = new HandlerThread("abp-private-handler"); 67 PRIVATE_HANDLER_THREAD = new HandlerThread("abp-private-handler");
54 PRIVATE_HANDLER_THREAD.setDaemon(true); 68 PRIVATE_HANDLER_THREAD.setDaemon(true);
55 PRIVATE_HANDLER_THREAD.start(); 69 PRIVATE_HANDLER_THREAD.start();
56 PRIVATE_HANDLER = new Handler(PRIVATE_HANDLER_THREAD.getLooper()); 70 PRIVATE_HANDLER = new Handler(PRIVATE_HANDLER_THREAD.getLooper());
57 71
58 GLOBAL_HANDLER_THREAD = new HandlerThread("abp-global-handler"); 72 GLOBAL_HANDLER_THREAD = new HandlerThread("abp-global-handler");
59 GLOBAL_HANDLER_THREAD.setDaemon(true); 73 GLOBAL_HANDLER_THREAD.setDaemon(true);
60 GLOBAL_HANDLER_THREAD.start(); 74 GLOBAL_HANDLER_THREAD.start();
61 GLOBAL_HANDLER = new Handler(GLOBAL_HANDLER_THREAD.getLooper()); 75 GLOBAL_HANDLER = new Handler(GLOBAL_HANDLER_THREAD.getLooper());
62 } 76 }
63 77
78 public static void init(Context context)
Felix Dahlke 2016/12/13 11:25:10 It makes me a bit nervous to rely on outside code
79 {
80 sharedPrefs = context.getSharedPreferences(AddOnBridge.class.getName(), Cont ext.MODE_PRIVATE);
81 startSyncRequests();
82 }
83
64 public static void postToHandler(Runnable runnable) 84 public static void postToHandler(Runnable runnable)
65 { 85 {
66 GLOBAL_HANDLER.post(runnable); 86 GLOBAL_HANDLER.post(runnable);
67 } 87 }
68 88
69 public static void postToHandlerDelayed(Runnable runnable, long delayMillis) 89 public static void postToHandlerDelayed(Runnable runnable, long delayMillis)
70 { 90 {
71 GLOBAL_HANDLER.postDelayed(runnable, delayMillis); 91 GLOBAL_HANDLER.postDelayed(runnable, delayMillis);
72 } 92 }
73 93
94 public static void startSyncRequests()
Felix Dahlke 2016/12/13 11:25:10 Does this have to be public? But see my suggestion
95 {
96 postToHandler(new Runnable()
97 {
98 @Override
99 public void run()
100 {
101 SYNC_REQUESTS_SEMAPHORE.acquireUninterruptibly();
102 final String jsonString = sharedPrefs.getString(PENDING_SYNC_REQUESTS_KE Y, null);
103 PENDING_SYNC_REQUESTS.clear();
104 PENDING_SYNC_REQUESTS.addAll(jsonStringToRequestList(jsonString));
105 for (final ChainedRequest request : PENDING_SYNC_REQUESTS)
106 {
107 GeckoAppShell.sendRequestToGecko(request);
108 }
109 SYNC_REQUESTS_SEMAPHORE.release();
110 performSyncRequests();
111 }
112 });
113 }
114
115 private static void performSyncRequests()
116 {
117 SYNC_REQUESTS_SEMAPHORE.acquireUninterruptibly();
118 if (PENDING_SYNC_REQUESTS.isEmpty())
119 {
120 SYNC_REQUESTS_SEMAPHORE.release();
121 scheduleSyncRequests();
122 return;
123 }
124 queryValue(new AdblockPlusApiCallback()
125 {
126 @Override
127 public void onApiRequestSucceeded(NativeJSObject jsObject)
128 {
129 final boolean requestsSaved = AddOnBridge.getBooleanFromJsObject(jsObjec t, "value", false);
130 if (requestsSaved)
131 {
132 PENDING_SYNC_REQUESTS.clear();
133 storeStringPref(PENDING_SYNC_REQUESTS_KEY, requestListToJsonString(PEN DING_SYNC_REQUESTS));
134 }
135 SYNC_REQUESTS_SEMAPHORE.release();
136 scheduleSyncRequests();
137 }
138
139 @Override
140 public void onApiRequestFailed(String errorMessage)
141 {
142 SYNC_REQUESTS_SEMAPHORE.release();
143 scheduleSyncRequests();
144 }
145 }, "requestsSaved");
146 }
147
148 private static void scheduleSyncRequests()
149 {
150 postToHandlerDelayed(new Runnable()
151 {
152 @Override
153 public void run()
154 {
155 performSyncRequests();
156 }
157 }, SYNC_REQUESTS_DELAY);
158 }
159
160 private static void addPendingSyncRequest(final ChainedRequest chainedRequest)
161 {
162 postToHandler(new Runnable()
163 {
164 @Override
165 public void run()
166 {
167 SYNC_REQUESTS_SEMAPHORE.acquireUninterruptibly();
168 PENDING_SYNC_REQUESTS.add(chainedRequest);
169 storeStringPref(PENDING_SYNC_REQUESTS_KEY, requestListToJsonString(PENDI NG_SYNC_REQUESTS));
170 SYNC_REQUESTS_SEMAPHORE.release();
171 }
172 });
173 }
174
175 private static String requestListToJsonString(final List<ChainedRequest> reque stList)
176 {
177 if (requestList == null)
178 {
179 return null;
180 }
181 final JSONArray jsonArray = new JSONArray();
182 for (final ChainedRequest request : requestList)
183 {
184 jsonArray.put(request.value);
185 }
186 return jsonArray.toString();
187 }
188
189 private static List<ChainedRequest> jsonStringToRequestList(final String jsonS tring)
190 {
191 final List<ChainedRequest> requestList = new ArrayList<>();
192 if (jsonString == null)
193 {
194 return requestList;
195 }
196 try
197 {
198 final JSONArray jsonArray = new JSONArray(jsonString);
199 for (int i = 0; i < jsonArray.length(); i++)
200 {
201 final ChainedRequest request = new ChainedRequest(jsonArray.getJSONObjec t(i), null);
202 requestList.add(request);
203 }
204 }
205 catch (JSONException e)
206 {
207 }
208 return requestList;
209 }
210
211 private static void storeStringPref(String key, String value)
212 {
213 final SharedPreferences.Editor editor = sharedPrefs.edit();
214 editor.putString(key, value);
215 editor.commit();
216 }
217
74 public static boolean getBooleanFromJsObject(final NativeJSObject obj, final S tring name, 218 public static boolean getBooleanFromJsObject(final NativeJSObject obj, final S tring name,
75 final boolean defaultValue) 219 final boolean defaultValue)
76 { 220 {
77 try 221 try
78 { 222 {
79 return obj.getBoolean(name); 223 return obj.getBoolean(name);
80 } 224 }
81 catch (final Exception e) 225 catch (final Exception e)
82 { 226 {
83 return defaultValue; 227 return defaultValue;
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
142 GeckoAppShell.sendRequestToGecko( 286 GeckoAppShell.sendRequestToGecko(
143 new ChainedRequest( 287 new ChainedRequest(
144 createRequestData("get" + makeFirstCharacterUppercase(name)), 288 createRequestData("get" + makeFirstCharacterUppercase(name)),
145 callback)); 289 callback));
146 } 290 }
147 291
148 public static void setBoolean(final AdblockPlusApiCallback callback, final Str ing name, 292 public static void setBoolean(final AdblockPlusApiCallback callback, final Str ing name,
149 final boolean enable) 293 final boolean enable)
150 { 294 {
151 Log.d(TAG, "setBoolean " + enable + " for " + name); 295 Log.d(TAG, "setBoolean " + enable + " for " + name);
152 GeckoAppShell.sendRequestToGecko( 296 final ChainedRequest request = new ChainedRequest(createRequestData("set" + makeFirstCharacterUppercase(name), enable), callback);
153 new ChainedRequest( 297 GeckoAppShell.sendRequestToGecko(request);
154 createRequestData("set" + makeFirstCharacterUppercase(name), enable) , 298 addPendingSyncRequest(request);
155 callback));
156 } 299 }
157 300
158 private static void callFunction(final AdblockPlusApiCallback callback, final String name, 301 private static void callFunction(final AdblockPlusApiCallback callback, final String name,
159 final Map<String, Object> parameters) 302 final Map<String, Object> parameters)
160 { 303 {
304 // By default, requests are not synced
305 callFunction(callback, name, parameters, false);
306 }
307
308 private static void callFunction(final AdblockPlusApiCallback callback, final String name,
309 final Map<String, Object> parameters, boolean shouldSyncRequest)
310 {
161 final JSONObject requestData = createRequestData(name); 311 final JSONObject requestData = createRequestData(name);
162 try 312 try
163 { 313 {
164 for (Map.Entry<String, Object> entry : parameters.entrySet()) 314 for (Map.Entry<String, Object> entry : parameters.entrySet())
165 requestData.put(entry.getKey(), entry.getValue()); 315 requestData.put(entry.getKey(), entry.getValue());
166 } 316 }
167 catch (JSONException e) 317 catch (JSONException e)
168 { 318 {
169 // we're only adding sane objects 319 // we're only adding sane objects
170 Log.e(TAG, "Creating request data failed with: " + e.getMessage(), e); 320 Log.e(TAG, "Creating request data failed with: " + e.getMessage(), e);
171 } 321 }
172 GeckoAppShell.sendRequestToGecko(new ChainedRequest(requestData, callback)); 322 final ChainedRequest request = new ChainedRequest(requestData, callback);
323 GeckoAppShell.sendRequestToGecko(request);
324 if (shouldSyncRequest)
325 {
326 addPendingSyncRequest(request);
327 }
173 } 328 }
174 329
175 public static void querySubscriptionListStatus(final AdblockPlusApiCallback ca llback, 330 public static void querySubscriptionListStatus(final AdblockPlusApiCallback ca llback,
176 final String url) 331 final String url)
177 { 332 {
178 Log.d(TAG, "querySubscriptionListStatus for " + url); 333 Log.d(TAG, "querySubscriptionListStatus for " + url);
179 final Map<String, Object> parameters = new HashMap<String, Object>(); 334 final Map<String, Object> parameters = new HashMap<String, Object>();
180 parameters.put("url", url); 335 parameters.put("url", url);
181 callFunction(callback, "isSubscriptionListed", parameters); 336 callFunction(callback, "isSubscriptionListed", parameters);
182 } 337 }
183 338
184 public static void addSubscription(final AdblockPlusApiCallback callback, 339 public static void addSubscription(final AdblockPlusApiCallback callback,
185 final String url, final String title) 340 final String url, final String title)
186 { 341 {
187 Log.d(TAG, "addSubscription for " + url + " (" + title + ")"); 342 Log.d(TAG, "addSubscription for " + url + " (" + title + ")");
188 final Map<String, Object> parameters = new HashMap<String, Object>(); 343 final Map<String, Object> parameters = new HashMap<String, Object>();
189 parameters.put("url", url); 344 parameters.put("url", url);
190 if (title != null) 345 if (title != null)
191 { 346 {
192 parameters.put("title", title); 347 parameters.put("title", title);
193 } 348 }
194 callFunction(callback, "addSubscription", parameters); 349 callFunction(callback, "addSubscription", parameters, true);
195 } 350 }
196 351
197 public static void queryActiveSubscriptions(final AdblockPlusApiCallback callb ack) 352 public static void queryActiveSubscriptions(final AdblockPlusApiCallback callb ack)
198 { 353 {
199 Log.d(TAG, "queryActiveSubscriptions"); 354 Log.d(TAG, "queryActiveSubscriptions");
200 final Map<String, Object> parameters = new HashMap<String, Object>(); 355 final Map<String, Object> parameters = new HashMap<String, Object>();
201 callFunction(callback, "getActiveSubscriptions", parameters); 356 callFunction(callback, "getActiveSubscriptions", parameters);
202 } 357 }
203 358
204 public static void removeSubscription(final AdblockPlusApiCallback callback, 359 public static void removeSubscription(final AdblockPlusApiCallback callback,
205 final String url) 360 final String url)
206 { 361 {
207 Log.d(TAG, "removeSubscription for " + url); 362 Log.d(TAG, "removeSubscription for " + url);
208 final Map<String, Object> parameters = new HashMap<String, Object>(); 363 final Map<String, Object> parameters = new HashMap<String, Object>();
209 parameters.put("url", url); 364 parameters.put("url", url);
210 callFunction(callback, "removeSubscription", parameters); 365 callFunction(callback, "removeSubscription", parameters, true);
211 } 366 }
212 367
213 public static void queryIsLocal(final AdblockPlusApiCallback callback, 368 public static void queryIsLocal(final AdblockPlusApiCallback callback,
214 final String url) 369 final String url)
215 { 370 {
216 Log.d(TAG, "queryIsLocal for " + url); 371 Log.d(TAG, "queryIsLocal for " + url);
217 final Map<String, Object> parameters = new HashMap<String, Object>(); 372 final Map<String, Object> parameters = new HashMap<String, Object>();
218 parameters.put("url", url); 373 parameters.put("url", url);
219 callFunction(callback, "isLocal", parameters); 374 callFunction(callback, "isLocal", parameters);
220 } 375 }
221 376
222 public static void queryIsPageWhitelisted(final AdblockPlusApiCallback callbac k, 377 public static void queryIsPageWhitelisted(final AdblockPlusApiCallback callbac k,
223 final String url) 378 final String url)
224 { 379 {
225 Log.d(TAG, "queryIsPageWhitelisted for " + url); 380 Log.d(TAG, "queryIsPageWhitelisted for " + url);
226 final Map<String, Object> parameters = new HashMap<String, Object>(); 381 final Map<String, Object> parameters = new HashMap<String, Object>();
227 parameters.put("url", url); 382 parameters.put("url", url);
228 callFunction(callback, "isPageWhitelisted", parameters); 383 callFunction(callback, "isPageWhitelisted", parameters);
229 } 384 }
230 385
231 public static void whitelistSite(final AdblockPlusApiCallback callback, final String url, 386 public static void whitelistSite(final AdblockPlusApiCallback callback, final String url,
232 final boolean whitelisted) 387 final boolean whitelisted)
233 { 388 {
234 Log.d(TAG, "whitelistSite for " + url); 389 Log.d(TAG, "whitelistSite for " + url);
235 final Map<String, Object> parameters = new HashMap<String, Object>(); 390 final Map<String, Object> parameters = new HashMap<String, Object>();
236 parameters.put("url", url); 391 parameters.put("url", url);
237 parameters.put("whitelisted", whitelisted); 392 parameters.put("whitelisted", whitelisted);
238 callFunction(callback, "whitelistSite", parameters); 393 callFunction(callback, "whitelistSite", parameters, true);
239 } 394 }
240 395
241 private static class ChainedRequest extends GeckoRequest 396 private static class ChainedRequest extends GeckoRequest
242 { 397 {
243 private final JSONObject value; 398 private final JSONObject value;
244 private final AdblockPlusApiCallback apiCallback; 399 private final AdblockPlusApiCallback apiCallback;
245 private final boolean checkForFiltersLoaded; 400 private final boolean checkForFiltersLoaded;
246 private final long creationTime; 401 private final long creationTime;
247 402
248 public ChainedRequest(final JSONObject value, final AdblockPlusApiCallback c allback, 403 public ChainedRequest(final JSONObject value, final AdblockPlusApiCallback c allback,
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
357 this.invokeSuccessCallback(jsObject); 512 this.invokeSuccessCallback(jsObject);
358 } 513 }
359 else 514 else
360 { 515 {
361 this.invokeFailureCallback(jsObject); 516 this.invokeFailureCallback(jsObject);
362 } 517 }
363 } 518 }
364 } 519 }
365 } 520 }
366 } 521 }
OLDNEW
« adblockplus/Api.jsm ('K') | « mobile/android/base/GeckoApplication.java ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld