Bug 1145579 - Open tab queue file when fennec starts or resumes (r=mcomella)
authorMartyn Haigh <martyn.haigh@gmail.com>
Fri, 27 Mar 2015 11:40:49 +0000
changeset 264806 eda1b8828dfbd8caeafb370bb650d691135524c5
parent 264805 8b514f389bf700fd6488469db296b5be70aa7abf
child 264807 5541383f6c41ad872705f52aa7d30f7765bdfcc6
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcomella
bugs1145579
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1145579 - Open tab queue file when fennec starts or resumes (r=mcomella)
mobile/android/base/BrowserApp.java
mobile/android/base/GeckoProfile.java
mobile/android/base/tabqueue/TabQueueHelper.java
mobile/android/base/tabqueue/TabQueueService.java
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -1,45 +1,32 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.net.URLEncoder;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Vector;
-
-import org.json.JSONException;
-import org.json.JSONObject;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.DynamicToolbar.PinReason;
 import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
 import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
 import org.mozilla.gecko.Tabs.TabEvents;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.TransitionsTracker;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.SuggestedSites;
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.favicons.LoadFaviconTask;
 import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
 import org.mozilla.gecko.favicons.decoders.IconDirectoryEntry;
+import org.mozilla.gecko.firstrun.FirstrunPane;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
 import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
 import org.mozilla.gecko.gfx.LayerMarginsAnimator;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.health.BrowserHealthRecorder;
 import org.mozilla.gecko.health.BrowserHealthReporter;
@@ -52,28 +39,28 @@ import org.mozilla.gecko.home.HomePager.
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.HomePanelsManager;
 import org.mozilla.gecko.home.SearchEngine;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuItem;
 import org.mozilla.gecko.mozglue.ContextUtils;
 import org.mozilla.gecko.mozglue.ContextUtils.SafeIntent;
 import org.mozilla.gecko.mozglue.RobocopTarget;
-import org.mozilla.gecko.firstrun.FirstrunPane;
 import org.mozilla.gecko.overlays.ui.ShareDialog;
 import org.mozilla.gecko.preferences.ClearOnShutdownPref;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.prompts.Prompt;
 import org.mozilla.gecko.prompts.PromptListItem;
 import org.mozilla.gecko.sync.setup.SyncAccounts;
+import org.mozilla.gecko.tabqueue.TabQueueHelper;
 import org.mozilla.gecko.tabs.TabHistoryController;
+import org.mozilla.gecko.tabs.TabHistoryController.OnShowTabHistory;
 import org.mozilla.gecko.tabs.TabHistoryFragment;
 import org.mozilla.gecko.tabs.TabHistoryPage;
 import org.mozilla.gecko.tabs.TabsPanel;
-import org.mozilla.gecko.tabs.TabHistoryController.OnShowTabHistory;
 import org.mozilla.gecko.toolbar.AutocompleteHandler;
 import org.mozilla.gecko.toolbar.BrowserToolbar;
 import org.mozilla.gecko.toolbar.BrowserToolbar.TabEditingState;
 import org.mozilla.gecko.toolbar.ToolbarProgressView;
 import org.mozilla.gecko.util.ActivityUtils;
 import org.mozilla.gecko.util.Clipboard;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GamepadUtils;
@@ -134,16 +121,30 @@ import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.view.animation.Interpolator;
 import android.widget.ListView;
 import android.widget.RelativeLayout;
 import android.widget.Toast;
 import android.widget.ViewFlipper;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URLEncoder;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Vector;
 
 public class BrowserApp extends GeckoApp
                         implements TabsPanel.TabsLayoutChangeListener,
                                    PropertyAnimator.PropertyAnimationListener,
                                    View.OnKeyListener,
                                    LayerView.OnMetricsChangedListener,
                                    BrowserSearch.OnSearchListener,
                                    BrowserSearch.OnEditSuggestionListener,
@@ -900,16 +901,29 @@ public class BrowserApp extends GeckoApp
     }
 
     @Override
     public void onAttachedToWindow() {
         // We can't show the first run experience until Gecko has finished initialization (bug 1077583).
         checkFirstrun(this, new SafeIntent(getIntent()));
     }
 
+    private void processTabQueue() {
+        if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_ANDROID_TAB_QUEUE) {
+            ThreadUtils.postToBackgroundThread(new Runnable() {
+                @Override
+                public void run() {
+                    if (TabQueueHelper.shouldOpenTabQueueUrls(BrowserApp.this)) {
+                        TabQueueHelper.openQueuedUrls(BrowserApp.this, mProfile, TabQueueHelper.FILE_NAME);
+                    }
+                }
+            });
+        }
+    }
+
     @Override
     public void onResume() {
         super.onResume();
 
         final String args = ContextUtils.getStringExtra(getIntent(), "args");
         // If an external intent tries to start Fennec in guest mode, and it's not already
         // in guest mode, this will change modes before opening the url.
         // NOTE: OnResume is called twice sometimes when showing on the lock screen.
@@ -918,16 +932,18 @@ public class BrowserApp extends GeckoApp
         if (enableGuestSession != inGuestSession) {
             doRestart(getIntent());
             GeckoAppShell.gracefulExit();
             return;
         }
 
         EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener)this,
             "Prompt:ShowTop");
+
+        processTabQueue();
     }
 
     @Override
     public void onPause() {
         super.onPause();
         // Register for Prompt:ShowTop so we can foreground this activity even if it's hidden.
         EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this,
             "Prompt:ShowTop");
--- a/mobile/android/base/GeckoProfile.java
+++ b/mobile/android/base/GeckoProfile.java
@@ -675,16 +675,24 @@ public final class GeckoProfile {
                 read = fr.read(buf);
             }
             return sb.toString();
         } finally {
             fr.close();
         }
     }
 
+    public boolean deleteFileFromProfileDir(String fileName) throws IllegalArgumentException {
+        if (TextUtils.isEmpty(fileName)) {
+            throw new IllegalArgumentException("Filename cannot be empty.");
+        }
+        File file = new File(getDir(), fileName);
+        return file.delete();
+    }
+
     private boolean remove() {
         try {
             synchronized (this) {
                 final File dir = getDir();
                 if (dir.exists()) {
                     delete(dir);
                 }
 
--- a/mobile/android/base/tabqueue/TabQueueHelper.java
+++ b/mobile/android/base/tabqueue/TabQueueHelper.java
@@ -1,34 +1,41 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.tabqueue;
 
+import org.mozilla.gecko.BrowserApp;
+import org.mozilla.gecko.GeckoProfile;
+import org.mozilla.gecko.GeckoSharedPrefs;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.preferences.GeckoPreferences;
+import org.mozilla.gecko.util.ThreadUtils;
+
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.support.v4.app.NotificationCompat;
+import android.util.Log;
 import org.json.JSONArray;
-import org.mozilla.gecko.BrowserApp;
-import org.mozilla.gecko.GeckoProfile;
-import org.mozilla.gecko.R;
-import org.mozilla.gecko.util.ThreadUtils;
 
 public class TabQueueHelper {
     private static final String LOGTAG = "Gecko" + TabQueueHelper.class.getSimpleName();
 
     public static final String FILE_NAME = "tab_queue_url_list.json";
     public static final String LOAD_URLS_ACTION = "TAB_QUEUE_LOAD_URLS_ACTION";
     public static final int TAB_QUEUE_NOTIFICATION_ID = R.id.tabQueueNotification;
 
+    public static final String PREF_TAB_QUEUE_COUNT = "tab_queue_count";
+
     /**
      * Reads file and converts any content to JSON, adds passed in URL to the data and writes back to the file,
      * creating the file if it doesn't already exist.  This should not be run on the UI thread.
      *
      * @param profile
      * @param url      URL to add
      * @param filename filename to add URL to
      * @return the number of tabs currently queued
@@ -47,34 +54,83 @@ public class TabQueueHelper {
 
     /**
      * Displays a notification showing the total number of tabs queue.  If there is already a notification displayed, it
      * will be replaced.
      *
      * @param context
      * @param tabsQueued
      */
-    static public void showNotification(Context context, int tabsQueued) {
+    public static void showNotification(final Context context, final int tabsQueued) {
+        ThreadUtils.assertNotOnUiThread();
+
         Intent resultIntent = new Intent(context, BrowserApp.class);
         resultIntent.setAction(TabQueueHelper.LOAD_URLS_ACTION);
 
         PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, resultIntent, PendingIntent.FLAG_CANCEL_CURRENT);
 
         String title, text;
         final Resources resources = context.getResources();
-        if(tabsQueued == 1) {
+        if (tabsQueued == 1) {
             title = resources.getString(R.string.tab_queue_notification_title_singular);
             text = resources.getString(R.string.tab_queue_notification_text_singular);
         } else {
             title = resources.getString(R.string.tab_queue_notification_title_plural);
             text = resources.getString(R.string.tab_queue_notification_text_plural, tabsQueued);
         }
 
         NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
-                                                                   .setSmallIcon(R.drawable.ic_status_logo)
-                                                                   .setContentTitle(title)
-                                                                   .setContentText(text)
-                                                                   .setContentIntent(pendingIntent);
+                                                     .setSmallIcon(R.drawable.ic_status_logo)
+                                                     .setContentTitle(title)
+                                                     .setContentText(text)
+                                                     .setContentIntent(pendingIntent);
 
         NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
         notificationManager.notify(TabQueueHelper.TAB_QUEUE_NOTIFICATION_ID, builder.build());
     }
+
+    public static boolean shouldOpenTabQueueUrls(final Context context) {
+        ThreadUtils.assertNotOnUiThread();
+
+        // TODO: Use profile shared prefs when bug 1147925 gets fixed.
+        final SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
+
+        boolean tabQueueEnabled = prefs.getBoolean(GeckoPreferences.PREFS_TAB_QUEUE, false);
+        int tabsQueued = prefs.getInt(PREF_TAB_QUEUE_COUNT, 0);
+
+        return tabQueueEnabled && tabsQueued > 0;
+    }
+
+    private static int getTabQueueLength(final Context context) {
+        ThreadUtils.assertNotOnUiThread();
+
+        // TODO: Use profile shared prefs when bug 1147925 gets fixed.
+        final SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
+        return prefs.getInt(PREF_TAB_QUEUE_COUNT, 0);
+    }
+
+    public static void openQueuedUrls(final Context context, final GeckoProfile profile, final String filename) {
+        ThreadUtils.assertNotOnUiThread();
+
+        // Remove the notification.
+        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        notificationManager.cancel(TAB_QUEUE_NOTIFICATION_ID);
+
+        // exit early if we don't have any tabs queued
+        if (getTabQueueLength(context) < 1) {
+            return;
+        }
+
+        JSONArray jsonArray = profile.readJSONArrayFromFile(filename);
+
+        // TODO: Convert data to required format for gecko to process and send to Gecko - Bug 1146325
+
+        try {
+            profile.deleteFileFromProfileDir(filename);
+        } catch (IllegalArgumentException e) {
+            Log.e(LOGTAG, "Error deleting Tab Queue data file.", e);
+        }
+
+        // TODO: Use profile shared prefs when bug 1147925 gets fixed.
+        final SharedPreferences prefs = GeckoSharedPrefs.forApp(context);
+        prefs.edit().remove(PREF_TAB_QUEUE_COUNT).apply();
+    }
 }
\ No newline at end of file
--- a/mobile/android/base/tabqueue/TabQueueService.java
+++ b/mobile/android/base/tabqueue/TabQueueService.java
@@ -3,30 +3,32 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.tabqueue;
 
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.Button;
 import android.widget.TextView;
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.GeckoProfile;
+import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.mozglue.ContextUtils;
 
 
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
 
@@ -160,16 +162,23 @@ public class TabQueueService extends Ser
         // As we're doing disk IO, let's run this stuff in a separate thread.
         executorService.submit(new Runnable() {
             @Override
             public void run() {
                 Context applicationContext = getApplicationContext();
                 final GeckoProfile profile = GeckoProfile.get(applicationContext);
                 int tabsQueued = TabQueueHelper.queueURL(profile, intentData, filename);
                 TabQueueHelper.showNotification(applicationContext, tabsQueued);
+
+                // Store the number of URLs queued so that we don't have to read and process the file to see if we have
+                // any urls to open.
+                // TODO: Use profile shared prefs when bug 1147925 gets fixed.
+                final SharedPreferences prefs = GeckoSharedPrefs.forApp(applicationContext);
+
+                prefs.edit().putInt(TabQueueHelper.PREF_TAB_QUEUE_COUNT, tabsQueued).apply();
             }
         });
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
         tabQueueHandler = null;