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: Instead of pooling, adding observer for save operations performed by the extension Created Dec. 21, 2016, 2:22 p.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;
22 24
23 import android.annotation.SuppressLint; 25 import android.annotation.SuppressLint;
26 import android.content.Context;
27 import android.content.SharedPreferences;
24 import android.os.Handler; 28 import android.os.Handler;
25 import android.os.HandlerThread; 29 import android.os.HandlerThread;
26 import android.util.Log; 30 import android.util.Log;
27 31
32 import org.json.JSONArray;
28 import org.json.JSONException; 33 import org.json.JSONException;
29 import org.json.JSONObject; 34 import org.json.JSONObject;
35 import org.mozilla.gecko.EventDispatcher;
30 import org.mozilla.gecko.GeckoAppShell; 36 import org.mozilla.gecko.GeckoAppShell;
37 import org.mozilla.gecko.util.GeckoEventListener;
31 import org.mozilla.gecko.util.GeckoRequest; 38 import org.mozilla.gecko.util.GeckoRequest;
32 import org.mozilla.gecko.util.NativeJSObject; 39 import org.mozilla.gecko.util.NativeJSObject;
33 40
34 @SuppressLint("DefaultLocale") 41 @SuppressLint("DefaultLocale")
35 public class AddOnBridge 42 public class AddOnBridge
36 { 43 {
37 private static final String TAG = "AdblockBrowser.AddOnBridge"; 44 private static final String TAG = "AdblockBrowser.AddOnBridge";
38 private static final String REQUEST_NAME = "AdblockPlus:Api"; 45 private static final String REQUEST_NAME = "AdblockPlus:Api";
39 // Timeout for checking filter loading (in seconds) 46 // Timeout for checking filter loading (in seconds)
40 private static final int QUERY_GET_FILTERS_LOADED_TIMEOUT = 30; 47 private static final int QUERY_GET_FILTERS_LOADED_TIMEOUT = 30;
41 // How long to wait between retries (in milliseconds) 48 // How long to wait between retries (in milliseconds)
42 private static final int QUERY_GET_FILTERS_LOADED_DELAY = 500; 49 private static final int QUERY_GET_FILTERS_LOADED_DELAY = 500;
43 // Handler+HandlerThread for posting delayed re-tries without interfering with 50 // Handler+HandlerThread for posting delayed re-tries without interfering with
44 // other threads (e.g. the UI or Gecko thread) 51 // other threads (e.g. the UI or Gecko thread)
45 private static final HandlerThread PRIVATE_HANDLER_THREAD; 52 private static final HandlerThread PRIVATE_HANDLER_THREAD;
46 private static final Handler PRIVATE_HANDLER; 53 private static final Handler PRIVATE_HANDLER;
47 // Global handler, for e.g. UI tasks 54 // Global handler, for e.g. UI tasks
48 private static final HandlerThread GLOBAL_HANDLER_THREAD; 55 private static final HandlerThread GLOBAL_HANDLER_THREAD;
49 private static final Handler GLOBAL_HANDLER; 56 private static final Handler GLOBAL_HANDLER;
57 // Sometimes, the app is killed before the extension is able to save all chang es regarding
58 // AddOnBridge requests. Given that, we need to store the uncompleted requests on SharedPrefs,
59 // so we can resend them to the extension once the app restarts
60 // See https://issues.adblockplus.org/ticket/2853
61 private static final String ON_SAVE_EVENT = "Abb:OnSave";
62 private static final List<ChainedRequest> UNCOMPLETED_REQUESTS = new ArrayList <>();
63 private static final String UNCOMPLETED_REQUESTS_PREFS_KEY = "UNCOMPLETED_REQU ESTS_PREFS_KEY";
64
65 private static SharedPreferences sharedPrefs;
50 66
51 static 67 static
52 { 68 {
53 PRIVATE_HANDLER_THREAD = new HandlerThread("abp-private-handler"); 69 PRIVATE_HANDLER_THREAD = new HandlerThread("abp-private-handler");
54 PRIVATE_HANDLER_THREAD.setDaemon(true); 70 PRIVATE_HANDLER_THREAD.setDaemon(true);
55 PRIVATE_HANDLER_THREAD.start(); 71 PRIVATE_HANDLER_THREAD.start();
56 PRIVATE_HANDLER = new Handler(PRIVATE_HANDLER_THREAD.getLooper()); 72 PRIVATE_HANDLER = new Handler(PRIVATE_HANDLER_THREAD.getLooper());
57 73
58 GLOBAL_HANDLER_THREAD = new HandlerThread("abp-global-handler"); 74 GLOBAL_HANDLER_THREAD = new HandlerThread("abp-global-handler");
59 GLOBAL_HANDLER_THREAD.setDaemon(true); 75 GLOBAL_HANDLER_THREAD.setDaemon(true);
60 GLOBAL_HANDLER_THREAD.start(); 76 GLOBAL_HANDLER_THREAD.start();
61 GLOBAL_HANDLER = new Handler(GLOBAL_HANDLER_THREAD.getLooper()); 77 GLOBAL_HANDLER = new Handler(GLOBAL_HANDLER_THREAD.getLooper());
62 } 78 }
63 79
80 public static void init(Context context)
81 {
82 sharedPrefs = context.getSharedPreferences(AddOnBridge.class.getName(), Cont ext.MODE_PRIVATE);
83 EventDispatcher.getInstance().registerGeckoThreadListener(new AddOnEventList ener(), ON_SAVE_EVENT);
84 loadAndResendUncompletedRequests();
85 }
86
64 public static void postToHandler(Runnable runnable) 87 public static void postToHandler(Runnable runnable)
65 { 88 {
66 GLOBAL_HANDLER.post(runnable); 89 GLOBAL_HANDLER.post(runnable);
67 } 90 }
68 91
69 public static void postToHandlerDelayed(Runnable runnable, long delayMillis) 92 public static void postToHandlerDelayed(Runnable runnable, long delayMillis)
70 { 93 {
71 GLOBAL_HANDLER.postDelayed(runnable, delayMillis); 94 GLOBAL_HANDLER.postDelayed(runnable, delayMillis);
72 } 95 }
73 96
97 private static void loadAndResendUncompletedRequests()
98 {
99 postToHandler(new Runnable()
100 {
101 @Override
102 public void run()
103 {
104 final String jsonString = sharedPrefs.getString(UNCOMPLETED_REQUESTS_PRE FS_KEY, null);
105 UNCOMPLETED_REQUESTS.clear();
106 UNCOMPLETED_REQUESTS.addAll(jsonStringToRequestList(jsonString));
107 for (final ChainedRequest request : UNCOMPLETED_REQUESTS)
108 {
109 GeckoAppShell.sendRequestToGecko(request);
110 }
111 }
112 });
113 }
114
115 private static void clearUncompletedRequests()
116 {
117 postToHandler(new Runnable()
118 {
119 @Override
120 public void run()
121 {
122 if (!UNCOMPLETED_REQUESTS.isEmpty())
123 {
124 UNCOMPLETED_REQUESTS.clear();
125 storeStringPref(UNCOMPLETED_REQUESTS_PREFS_KEY, requestListToJsonStrin g(UNCOMPLETED_REQUESTS));
126 }
127 }
128 });
129 }
130
131 private static void addUncompletedRequest(final ChainedRequest chainedRequest)
132 {
133 postToHandler(new Runnable()
134 {
135 @Override
136 public void run()
137 {
138 UNCOMPLETED_REQUESTS.add(chainedRequest);
139 storeStringPref(UNCOMPLETED_REQUESTS_PREFS_KEY, requestListToJsonString( UNCOMPLETED_REQUESTS));
140 }
141 });
142 }
143
144 private static String requestListToJsonString(final List<ChainedRequest> reque stList)
145 {
146 if (requestList == null)
147 {
148 return null;
149 }
150 final JSONArray jsonArray = new JSONArray();
151 for (final ChainedRequest request : requestList)
152 {
153 jsonArray.put(request.value);
154 }
155 return jsonArray.toString();
156 }
157
158 private static List<ChainedRequest> jsonStringToRequestList(final String jsonS tring)
159 {
160 final List<ChainedRequest> requestList = new ArrayList<>();
161 if (jsonString == null)
162 {
163 return requestList;
164 }
165 try
166 {
167 final JSONArray jsonArray = new JSONArray(jsonString);
168 for (int i = 0; i < jsonArray.length(); i++)
169 {
170 final ChainedRequest request = new ChainedRequest(jsonArray.getJSONObjec t(i), null);
171 requestList.add(request);
172 }
173 }
174 catch (JSONException e)
175 {
176 }
177 return requestList;
178 }
179
180 private static void storeStringPref(String key, String value)
181 {
182 final SharedPreferences.Editor editor = sharedPrefs.edit();
183 editor.putString(key, value);
184 editor.commit();
185 }
186
74 public static boolean getBooleanFromJsObject(final NativeJSObject obj, final S tring name, 187 public static boolean getBooleanFromJsObject(final NativeJSObject obj, final S tring name,
75 final boolean defaultValue) 188 final boolean defaultValue)
76 { 189 {
77 try 190 try
78 { 191 {
79 return obj.getBoolean(name); 192 return obj.getBoolean(name);
80 } 193 }
81 catch (final Exception e) 194 catch (final Exception e)
82 { 195 {
83 return defaultValue; 196 return defaultValue;
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
142 GeckoAppShell.sendRequestToGecko( 255 GeckoAppShell.sendRequestToGecko(
143 new ChainedRequest( 256 new ChainedRequest(
144 createRequestData("get" + makeFirstCharacterUppercase(name)), 257 createRequestData("get" + makeFirstCharacterUppercase(name)),
145 callback)); 258 callback));
146 } 259 }
147 260
148 public static void setBoolean(final AdblockPlusApiCallback callback, final Str ing name, 261 public static void setBoolean(final AdblockPlusApiCallback callback, final Str ing name,
149 final boolean enable) 262 final boolean enable)
150 { 263 {
151 Log.d(TAG, "setBoolean " + enable + " for " + name); 264 Log.d(TAG, "setBoolean " + enable + " for " + name);
152 GeckoAppShell.sendRequestToGecko( 265 final ChainedRequest request =
153 new ChainedRequest( 266 new ChainedRequest(createRequestData("set" + makeFirstCharacterUppercase (name), enable), callback);
154 createRequestData("set" + makeFirstCharacterUppercase(name), enable) , 267 GeckoAppShell.sendRequestToGecko(request);
155 callback)); 268 addUncompletedRequest(request);
156 } 269 }
157 270
158 private static void callFunction(final AdblockPlusApiCallback callback, final String name, 271 private static void callFunction(final AdblockPlusApiCallback callback, final String name,
159 final Map<String, Object> parameters) 272 final Map<String, Object> parameters)
160 { 273 {
274 // By default, requests are not added to the uncompleted request list. This should apply for
275 // requests that doesn't result in save operations performed by the extensio n
276 callFunction(callback, name, parameters, false);
277 }
278
279 private static void callFunction(final AdblockPlusApiCallback callback, final String name,
280 final Map<String, Object> parameters, boolean shouldAddUncompletedRequest)
Felix Dahlke 2016/12/21 20:23:25 Nit: `shouldAddUncompletedRequest` sounds a bit li
diegocarloslima 2016/12/21 20:29:31 Acknowledged.
281 {
161 final JSONObject requestData = createRequestData(name); 282 final JSONObject requestData = createRequestData(name);
162 try 283 try
163 { 284 {
164 for (Map.Entry<String, Object> entry : parameters.entrySet()) 285 for (Map.Entry<String, Object> entry : parameters.entrySet())
165 requestData.put(entry.getKey(), entry.getValue()); 286 requestData.put(entry.getKey(), entry.getValue());
166 } 287 }
167 catch (JSONException e) 288 catch (JSONException e)
168 { 289 {
169 // we're only adding sane objects 290 // we're only adding sane objects
170 Log.e(TAG, "Creating request data failed with: " + e.getMessage(), e); 291 Log.e(TAG, "Creating request data failed with: " + e.getMessage(), e);
171 } 292 }
172 GeckoAppShell.sendRequestToGecko(new ChainedRequest(requestData, callback)); 293 final ChainedRequest request = new ChainedRequest(requestData, callback);
294 GeckoAppShell.sendRequestToGecko(request);
295 if (shouldAddUncompletedRequest)
296 {
297 addUncompletedRequest(request);
298 }
173 } 299 }
174 300
175 public static void querySubscriptionListStatus(final AdblockPlusApiCallback ca llback, 301 public static void querySubscriptionListStatus(final AdblockPlusApiCallback ca llback,
176 final String url) 302 final String url)
177 { 303 {
178 Log.d(TAG, "querySubscriptionListStatus for " + url); 304 Log.d(TAG, "querySubscriptionListStatus for " + url);
179 final Map<String, Object> parameters = new HashMap<String, Object>(); 305 final Map<String, Object> parameters = new HashMap<String, Object>();
180 parameters.put("url", url); 306 parameters.put("url", url);
181 callFunction(callback, "isSubscriptionListed", parameters); 307 callFunction(callback, "isSubscriptionListed", parameters);
182 } 308 }
183 309
184 public static void addSubscription(final AdblockPlusApiCallback callback, 310 public static void addSubscription(final AdblockPlusApiCallback callback,
185 final String url, final String title) 311 final String url, final String title)
186 { 312 {
187 Log.d(TAG, "addSubscription for " + url + " (" + title + ")"); 313 Log.d(TAG, "addSubscription for " + url + " (" + title + ")");
188 final Map<String, Object> parameters = new HashMap<String, Object>(); 314 final Map<String, Object> parameters = new HashMap<String, Object>();
189 parameters.put("url", url); 315 parameters.put("url", url);
190 if (title != null) 316 if (title != null)
191 { 317 {
192 parameters.put("title", title); 318 parameters.put("title", title);
193 } 319 }
194 callFunction(callback, "addSubscription", parameters); 320 callFunction(callback, "addSubscription", parameters, true);
195 } 321 }
196 322
197 public static void queryActiveSubscriptions(final AdblockPlusApiCallback callb ack) 323 public static void queryActiveSubscriptions(final AdblockPlusApiCallback callb ack)
198 { 324 {
199 Log.d(TAG, "queryActiveSubscriptions"); 325 Log.d(TAG, "queryActiveSubscriptions");
200 final Map<String, Object> parameters = new HashMap<String, Object>(); 326 final Map<String, Object> parameters = new HashMap<String, Object>();
201 callFunction(callback, "getActiveSubscriptions", parameters); 327 callFunction(callback, "getActiveSubscriptions", parameters);
202 } 328 }
203 329
204 public static void removeSubscription(final AdblockPlusApiCallback callback, 330 public static void removeSubscription(final AdblockPlusApiCallback callback,
205 final String url) 331 final String url)
206 { 332 {
207 Log.d(TAG, "removeSubscription for " + url); 333 Log.d(TAG, "removeSubscription for " + url);
208 final Map<String, Object> parameters = new HashMap<String, Object>(); 334 final Map<String, Object> parameters = new HashMap<String, Object>();
209 parameters.put("url", url); 335 parameters.put("url", url);
210 callFunction(callback, "removeSubscription", parameters); 336 callFunction(callback, "removeSubscription", parameters, true);
211 } 337 }
212 338
213 public static void queryIsLocal(final AdblockPlusApiCallback callback, 339 public static void queryIsLocal(final AdblockPlusApiCallback callback,
214 final String url) 340 final String url)
215 { 341 {
216 Log.d(TAG, "queryIsLocal for " + url); 342 Log.d(TAG, "queryIsLocal for " + url);
217 final Map<String, Object> parameters = new HashMap<String, Object>(); 343 final Map<String, Object> parameters = new HashMap<String, Object>();
218 parameters.put("url", url); 344 parameters.put("url", url);
219 callFunction(callback, "isLocal", parameters); 345 callFunction(callback, "isLocal", parameters);
220 } 346 }
221 347
222 public static void queryIsPageWhitelisted(final AdblockPlusApiCallback callbac k, 348 public static void queryIsPageWhitelisted(final AdblockPlusApiCallback callbac k,
223 final String url) 349 final String url)
224 { 350 {
225 Log.d(TAG, "queryIsPageWhitelisted for " + url); 351 Log.d(TAG, "queryIsPageWhitelisted for " + url);
226 final Map<String, Object> parameters = new HashMap<String, Object>(); 352 final Map<String, Object> parameters = new HashMap<String, Object>();
227 parameters.put("url", url); 353 parameters.put("url", url);
228 callFunction(callback, "isPageWhitelisted", parameters); 354 callFunction(callback, "isPageWhitelisted", parameters);
229 } 355 }
230 356
231 public static void whitelistSite(final AdblockPlusApiCallback callback, final String url, 357 public static void whitelistSite(final AdblockPlusApiCallback callback, final String url,
232 final boolean whitelisted) 358 final boolean whitelisted)
233 { 359 {
234 Log.d(TAG, "whitelistSite for " + url); 360 Log.d(TAG, "whitelistSite for " + url);
235 final Map<String, Object> parameters = new HashMap<String, Object>(); 361 final Map<String, Object> parameters = new HashMap<String, Object>();
236 parameters.put("url", url); 362 parameters.put("url", url);
237 parameters.put("whitelisted", whitelisted); 363 parameters.put("whitelisted", whitelisted);
238 callFunction(callback, "whitelistSite", parameters); 364 callFunction(callback, "whitelistSite", parameters, true);
239 } 365 }
240 366
241 private static class ChainedRequest extends GeckoRequest 367 private static class ChainedRequest extends GeckoRequest
242 { 368 {
243 private final JSONObject value; 369 private final JSONObject value;
244 private final AdblockPlusApiCallback apiCallback; 370 private final AdblockPlusApiCallback apiCallback;
245 private final boolean checkForFiltersLoaded; 371 private final boolean checkForFiltersLoaded;
246 private final long creationTime; 372 private final long creationTime;
247 373
248 public ChainedRequest(final JSONObject value, final AdblockPlusApiCallback c allback, 374 public ChainedRequest(final JSONObject value, final AdblockPlusApiCallback c allback,
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 { 482 {
357 this.invokeSuccessCallback(jsObject); 483 this.invokeSuccessCallback(jsObject);
358 } 484 }
359 else 485 else
360 { 486 {
361 this.invokeFailureCallback(jsObject); 487 this.invokeFailureCallback(jsObject);
362 } 488 }
363 } 489 }
364 } 490 }
365 } 491 }
492
493 private static class AddOnEventListener implements GeckoEventListener
494 {
495 @Override
496 public void handleMessage(String event, JSONObject message)
497 {
498 if (ON_SAVE_EVENT.equals(event))
499 {
500 // This indicates that all changes have been saved by the extension. Tha t way, we can clear
501 // our list of uncompleted requests
502 // See https://issues.adblockplus.org/ticket/2853
503 clearUncompletedRequests();
504 }
505 }
506 }
366 } 507 }
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