1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/GlobalHistory.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,123 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko; 1.10 + 1.11 +import org.mozilla.gecko.db.BrowserDB; 1.12 +import org.mozilla.gecko.util.ThreadUtils; 1.13 + 1.14 +import android.database.Cursor; 1.15 +import android.net.Uri; 1.16 +import android.os.Handler; 1.17 +import android.util.Log; 1.18 + 1.19 +import java.lang.ref.SoftReference; 1.20 +import java.util.HashSet; 1.21 +import java.util.LinkedList; 1.22 +import java.util.Queue; 1.23 +import java.util.Set; 1.24 + 1.25 +class GlobalHistory { 1.26 + private static final String LOGTAG = "GeckoGlobalHistory"; 1.27 + 1.28 + private static GlobalHistory sInstance = new GlobalHistory(); 1.29 + 1.30 + static GlobalHistory getInstance() { 1.31 + return sInstance; 1.32 + } 1.33 + 1.34 + // this is the delay between receiving a URI check request and processing it. 1.35 + // this allows batching together multiple requests and processing them together, 1.36 + // which is more efficient. 1.37 + private static final long BATCHING_DELAY_MS = 100; 1.38 + 1.39 + private final Handler mHandler; // a background thread on which we can process requests 1.40 + private final Queue<String> mPendingUris; // URIs that need to be checked 1.41 + private SoftReference<Set<String>> mVisitedCache; // cache of the visited URI list 1.42 + private final Runnable mNotifierRunnable; // off-thread runnable used to check URIs 1.43 + private boolean mProcessing; // = false // whether or not the runnable is queued/working 1.44 + 1.45 + private GlobalHistory() { 1.46 + mHandler = ThreadUtils.getBackgroundHandler(); 1.47 + mPendingUris = new LinkedList<String>(); 1.48 + mVisitedCache = new SoftReference<Set<String>>(null); 1.49 + mNotifierRunnable = new Runnable() { 1.50 + @Override 1.51 + public void run() { 1.52 + Set<String> visitedSet = mVisitedCache.get(); 1.53 + if (visitedSet == null) { 1.54 + // the cache was wiped away, repopulate it 1.55 + Log.w(LOGTAG, "Rebuilding visited link set..."); 1.56 + visitedSet = new HashSet<String>(); 1.57 + Cursor c = null; 1.58 + try { 1.59 + c = BrowserDB.getAllVisitedHistory(GeckoAppShell.getContext().getContentResolver()); 1.60 + if (c == null) { 1.61 + return; 1.62 + } 1.63 + 1.64 + if (c.moveToFirst()) { 1.65 + do { 1.66 + visitedSet.add(c.getString(0)); 1.67 + } while (c.moveToNext()); 1.68 + } 1.69 + mVisitedCache = new SoftReference<Set<String>>(visitedSet); 1.70 + } finally { 1.71 + if (c != null) 1.72 + c.close(); 1.73 + } 1.74 + } 1.75 + 1.76 + // this runs on the same handler thread as the checkUriVisited code, 1.77 + // so no synchronization needed 1.78 + while (true) { 1.79 + String uri = mPendingUris.poll(); 1.80 + if (uri == null) { 1.81 + break; 1.82 + } 1.83 + if (visitedSet.contains(uri)) { 1.84 + GeckoAppShell.notifyUriVisited(uri); 1.85 + } 1.86 + } 1.87 + mProcessing = false; 1.88 + } 1.89 + }; 1.90 + } 1.91 + 1.92 + public void addToGeckoOnly(String uri) { 1.93 + Set<String> visitedSet = mVisitedCache.get(); 1.94 + if (visitedSet != null) { 1.95 + visitedSet.add(uri); 1.96 + } 1.97 + GeckoAppShell.notifyUriVisited(uri); 1.98 + } 1.99 + 1.100 + public void add(String uri) { 1.101 + BrowserDB.updateVisitedHistory(GeckoAppShell.getContext().getContentResolver(), uri); 1.102 + addToGeckoOnly(uri); 1.103 + } 1.104 + 1.105 + public void update(String uri, String title) { 1.106 + BrowserDB.updateHistoryTitle(GeckoAppShell.getContext().getContentResolver(), uri, title); 1.107 + } 1.108 + 1.109 + public void checkUriVisited(final String uri) { 1.110 + mHandler.post(new Runnable() { 1.111 + @Override 1.112 + public void run() { 1.113 + // this runs on the same handler thread as the processing loop, 1.114 + // so no synchronization needed 1.115 + mPendingUris.add(uri); 1.116 + if (mProcessing) { 1.117 + // there's already a runnable queued up or working away, so 1.118 + // no need to post another 1.119 + return; 1.120 + } 1.121 + mProcessing = true; 1.122 + mHandler.postDelayed(mNotifierRunnable, BATCHING_DELAY_MS); 1.123 + } 1.124 + }); 1.125 + } 1.126 +}