Bug 1413739 - Part 1 - Offer Firefox itself as an Assist App. r=nechen
authorJan Henning <jh+bugzilla@buttercookie.de>
Sun, 03 Dec 2017 21:16:03 +0100
changeset 394953 151826976a7d1cc0773017bdbd1eaa8554fc247e
parent 394952 4a160faf5ac5f9b3561e945344e8db501b74ad45
child 394954 92d7837be2dbb28c5648282fb61843c43d2b00bd
push id97987
push usernerli@mozilla.com
push dateTue, 05 Dec 2017 13:52:50 +0000
treeherdermozilla-inbound@8842dba7396b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnechen
bugs1413739, 1210242
milestone59.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 1413739 - Part 1 - Offer Firefox itself as an Assist App. r=nechen The technique for setting our icon is just a straight reimplementation of bug 1210242. Because of the way the new tab might be opened from within a processActionViewIntent Runnable, we can't enter editing mode by simply listening for an ACTION_ASSIST intent from within BrowserApp, as we need to enter editing mode *after* the correct tab has already been opened and selected and BrowserApp doesn't get any hint on when that Runnable might have run. Instead, we introduce a new tab event, so we can trigger editing mode at the right time via the tab itself. MozReview-Commit-ID: 8Bvv5TXyhhI
mobile/android/app/src/main/res/values-v21/integers.xml
mobile/android/app/src/main/res/values/integers.xml
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
mobile/android/base/java/org/mozilla/gecko/Tabs.java
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/main/res/values-v21/integers.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<resources>
+
+    <integer name="assist_launch_icon_res">@drawable/icon</integer>
+
+</resources>
--- a/mobile/android/app/src/main/res/values/integers.xml
+++ b/mobile/android/app/src/main/res/values/integers.xml
@@ -8,10 +8,11 @@
     <integer name="number_of_top_sites">6</integer>
     <integer name="number_of_top_sites_cols">2</integer>
     <integer name="max_icon_grid_columns">4</integer>
     <integer name="panel_icon_grid_view_columns">3</integer>
     <integer name="number_of_inline_share_devices">2</integer>
     <integer name="max_search_suggestions">2</integer>
     <integer name="max_saved_suggestions">2</integer>
     <integer name="shift_duration_suggestion">500</integer>
+    <integer name="assist_launch_icon_res">0</integer>
 
 </resources>
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -137,16 +137,32 @@
                 <action android:name="android.intent.action.WEB_SEARCH" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
                 <data android:scheme="" />
                 <data android:scheme="http" />
                 <data android:scheme="https" />
             </intent-filter>
 
+            <intent-filter>
+                <action android:name="android.intent.action.ASSIST"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+
+            <!-- Pre Lollipop devices display a generic search icon, if none is
+                 provided here. To use this we need to set the resource to 0.
+                 For Lollipop and later the search launcher icon ist used.
+                 To retrieve the resource value the Bundle.getInt() method is
+                 used, so we use integer resources instead of drawables, because
+                 setting a drawable referenced to 0 results in errors when used
+                 as a real drawable resource somewhere else. -->
+            <meta-data
+                android:name="com.android.systemui.action_assist_icon"
+                android:resource="@integer/assist_launch_icon_res"/>
+
             <!-- For XPI installs from websites and the download manager. -->
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <data android:scheme="file" />
                 <data android:scheme="http" />
                 <data android:scheme="https" />
                 <data android:mimeType="application/x-xpinstall" />
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -430,25 +430,27 @@ public class BrowserApp extends GeckoApp
             case MENU_UPDATED:
                 if (Tabs.getInstance().isSelectedTab(tab)) {
                     invalidateOptionsMenu();
                 }
                 break;
             case PAGE_SHOW:
                 tab.loadFavicon();
                 break;
-
             case UNSELECTED:
                 // We receive UNSELECTED immediately after the SELECTED listeners run
                 // so we are ensured that the unselectedTabEditingText has not changed.
                 if (tab.isEditing()) {
                     // Copy to avoid constructing new objects.
                     tab.getEditingState().copyFrom(mLastTabEditingState);
                 }
                 break;
+            case START_EDITING:
+                enterEditingMode();
+                break;
         }
 
         if (HardwareUtils.isTablet() && msg == TabEvents.SELECTED) {
             updateEditingModeForTab(tab);
         }
 
         super.onTabChanged(tab, msg, data);
     }
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -1406,18 +1406,20 @@ public abstract class GeckoApp extends G
 
         mWasFirstTabShownAfterActivityUnhidden = true; // Reset since we'll be loading a tab.
 
         final SafeIntent intent = new SafeIntent(getIntent());
         final String action = intent.getAction();
 
         final String passedUri = getIntentURI(intent);
 
-        final boolean isExternalURL = passedUri != null;
-        final boolean isAboutHomeURL = isExternalURL && AboutPages.isAboutHome(passedUri);
+        final boolean intentHasURL = passedUri != null;
+        final boolean isAboutHomeURL = intentHasURL && AboutPages.isAboutHome(passedUri);
+        final boolean isAssistIntent = Intent.ACTION_ASSIST.equals(action);
+        final boolean needsNewForegroundTab = intentHasURL || isAssistIntent;
 
         // Start migrating as early as possible, can do this in
         // parallel with Gecko load.
         checkMigrateProfile();
 
         initializeChrome();
 
         // We need to wait here because mShouldRestore can revert back to
@@ -1433,24 +1435,26 @@ public abstract class GeckoApp extends G
             }
         }
 
         if (mIsRestoringActivity && hasGeckoTab(intent)) {
             Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
             handleSelectTabIntent(intent);
         // External URLs and new tab from widget should always be loaded regardless of whether Gecko is
         // already running.
-        } else if (isExternalURL) {
+        } else if (needsNewForegroundTab) {
             // Restore tabs before opening an external URL so that the new tab
             // is animated properly.
             Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
             processActionViewIntent(new Runnable() {
                 @Override
                 public void run() {
-                    if (isAboutHomeURL) {
+                    if (isAssistIntent) {
+                        Tabs.getInstance().addTab(Tabs.LOADURL_START_EDITING | Tabs.LOADURL_EXTERNAL);
+                    } else if (isAboutHomeURL) {
                         // respect the user preferences for about:home from external intent calls
                         loadStartupTab(Tabs.LOADURL_NEW_TAB, action);
                     } else {
                         final int flags = getNewTabFlags();
                         loadStartupTab(passedUri, intent, flags);
                     }
                 }
             });
@@ -1775,16 +1779,18 @@ public abstract class GeckoApp extends G
                     final String url = intent.getDataString();
                     int flags = Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_USER_ENTERED | Tabs.LOADURL_EXTERNAL;
                     if (isFirstTab) {
                         flags |= Tabs.LOADURL_FIRST_AFTER_ACTIVITY_UNHIDDEN;
                     }
                     Tabs.getInstance().loadUrlWithIntentExtras(url, intent, flags);
                 }
             });
+        } else if (Intent.ACTION_ASSIST.equals(action)) {
+            Tabs.getInstance().addTab(Tabs.LOADURL_START_EDITING | Tabs.LOADURL_EXTERNAL);
         } else if (ACTION_HOMESCREEN_SHORTCUT.equals(action)) {
             final GeckoBundle data = new GeckoBundle(2);
             data.putString("uri", uri);
             data.putString("flags", "OPEN_SWITCHTAB");
             getAppEventDispatcher().dispatch("Tab:OpenUri", data);
         } else if (Intent.ACTION_SEARCH.equals(action)) {
             final GeckoBundle data = new GeckoBundle(2);
             data.putString("uri", uri);
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -79,16 +79,18 @@ public class Tabs implements BundleEvent
     public static final int LOADURL_PINNED       = 1 << 3;
     public static final int LOADURL_DELAY_LOAD   = 1 << 4;
     public static final int LOADURL_DESKTOP      = 1 << 5;
     public static final int LOADURL_BACKGROUND   = 1 << 6;
     /** Indicates the url has been specified by a source external to the app. */
     public static final int LOADURL_EXTERNAL     = 1 << 7;
     /** Indicates the tab is the first shown after Firefox is hidden and restored. */
     public static final int LOADURL_FIRST_AFTER_ACTIVITY_UNHIDDEN = 1 << 8;
+    /** Indicates that we should enter editing mode after opening the tab. */
+    public static final int LOADURL_START_EDITING = 1 << 9;
 
     private static final long PERSIST_TABS_AFTER_MILLISECONDS = 1000 * 2;
 
     public static final int INVALID_TAB_ID = -1;
     // Used to indicate a new tab should be appended to the current tabs.
     public static final int NEW_LAST_INDEX = -1;
 
     private static final AtomicInteger sTabId = new AtomicInteger(0);
@@ -753,17 +755,18 @@ public class Tabs implements BundleEvent
         SECURITY_CHANGE,
         DESKTOP_MODE_CHANGE,
         RECORDING_CHANGE,
         BOOKMARK_ADDED,
         BOOKMARK_REMOVED,
         AUDIO_PLAYING_CHANGE,
         OPENED_FROM_TABS_TRAY,
         MEDIA_PLAYING_CHANGE,
-        MEDIA_PLAYING_RESUME
+        MEDIA_PLAYING_RESUME,
+        START_EDITING,
     }
 
     public void notifyListeners(Tab tab, TabEvents msg) {
         notifyListeners(tab, msg, "");
     }
 
     public void notifyListeners(final Tab tab, final TabEvents msg, final String data) {
         if (tab == null &&
@@ -982,16 +985,17 @@ public class Tabs implements BundleEvent
         // delayLoad implies background tab
         boolean background = delayLoad || (flags & LOADURL_BACKGROUND) != 0;
 
         boolean isPrivate = (flags & LOADURL_PRIVATE) != 0 || (intent != null && intent.getBooleanExtra(PRIVATE_TAB_INTENT_EXTRA, false));
         boolean userEntered = (flags & LOADURL_USER_ENTERED) != 0;
         boolean desktopMode = (flags & LOADURL_DESKTOP) != 0;
         boolean external = (flags & LOADURL_EXTERNAL) != 0;
         final boolean isFirstShownAfterActivityUnhidden = (flags & LOADURL_FIRST_AFTER_ACTIVITY_UNHIDDEN) != 0;
+        final boolean startEditing = (flags & LOADURL_START_EDITING) != 0;
 
         data.putString("url", url);
         data.putString("engine", searchEngine);
         data.putInt("parentId", parentId);
         data.putBoolean("userEntered", userEntered);
         data.putBoolean("isPrivate", isPrivate);
         data.putBoolean("pinned", (flags & LOADURL_PINNED) != 0);
         data.putBoolean("desktopMode", desktopMode);
@@ -1060,36 +1064,50 @@ public class Tabs implements BundleEvent
         if (tabToSelect == null) {
             return null;
         }
 
         if (!delayLoad && !background) {
             selectTab(tabToSelect.getId());
         }
 
+        if (startEditing) {
+            notifyListeners(tabToSelect, TabEvents.START_EDITING);
+        }
+
         // Load favicon instantly for about:home page because it's already cached
         if (AboutPages.isBuiltinIconPage(url)) {
             tabToSelect.loadFavicon();
         }
 
-
         return tabToSelect;
     }
 
     /**
-     * Opens a new tab and loads either about:home or, if PREFS_HOMEPAGE_FOR_EVERY_NEW_TAB is set,
-     * the user's homepage.
+     * Opens a new tab and loads a page according to the user's preferences (by default about:home).
      */
     @RobocopTarget
     public Tab addTab() {
-        return loadUrl(getHomepageForNewTab(mAppContext), Tabs.LOADURL_NEW_TAB);
+        return addTab(Tabs.LOADURL_NONE);
     }
 
+    /**
+     * Opens a new tab and loads a page according to the user's preferences (by default about:home).
+     */
     public Tab addPrivateTab() {
-        return loadUrl(getHomepageForNewTab(mAppContext), Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_PRIVATE);
+        return addTab(Tabs.LOADURL_PRIVATE);
+    }
+
+    /**
+     * Opens a new tab and loads a page according to the user's preferences (by default about:home).
+     *
+     * @param flags additional flags used when opening the tab
+     */
+    public Tab addTab(int flags) {
+        return loadUrl(getHomepageForNewTab(mAppContext), flags | Tabs.LOADURL_NEW_TAB);
     }
 
     /**
      * Open the url as a new tab, and mark the selected tab as its "parent".
      *
      * If the url is already open in a tab, the existing tab is selected.
      * Use this for tabs opened by the browser chrome, so users can press the
      * "Back" button to return to the previous tab.