Bug 1526929 Search Widget for Fennec Part 2 - Wire search widget component with the main component r=petru
authorAndrei Lazar <andrei.a.lazar@softvision.ro>
Fri, 08 Mar 2019 15:04:23 +0000
changeset 521247 0368c4ca4ece
parent 521246 bde7ebdef8a4
child 521248 a32961795869
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspetru
bugs1526929
milestone67.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 1526929 Search Widget for Fennec Part 2 - Wire search widget component with the main component r=petru Implemented search widget's intent handling in BrowserApp class. Depends on D20149 Differential Revision: https://phabricator.services.mozilla.com/D20151
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/Tabs.java
mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbar.java
mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarEditLayout.java
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -84,16 +84,17 @@ import org.mozilla.gecko.delegates.Brows
 import org.mozilla.gecko.delegates.OfflineTabStatusDelegate;
 import org.mozilla.gecko.delegates.ScreenshotDelegate;
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.distribution.DistributionStoreCallback;
 import org.mozilla.gecko.dlc.DlcStudyService;
 import org.mozilla.gecko.dlc.DlcSyncService;
 import org.mozilla.gecko.extensions.ExtensionPermissionsHelper;
 import org.mozilla.gecko.firstrun.OnboardingHelper;
+import org.mozilla.gecko.search.SearchWidgetProvider;
 import org.mozilla.geckoview.DynamicToolbarAnimator.PinReason;
 import org.mozilla.gecko.home.BrowserSearch;
 import org.mozilla.gecko.home.HomeBanner;
 import org.mozilla.gecko.home.HomeConfig;
 import org.mozilla.gecko.home.HomeConfig.PanelType;
 import org.mozilla.gecko.home.HomeConfigPrefsBackend;
 import org.mozilla.gecko.home.HomeFragment;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenInBackgroundListener;
@@ -177,17 +178,23 @@ import java.net.URLEncoder;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.regex.Pattern;
 
+import static org.mozilla.gecko.Tabs.LOADURL_DELAY_LOAD;
+import static org.mozilla.gecko.Tabs.LOADURL_EXTERNAL;
+import static org.mozilla.gecko.Tabs.LOADURL_PINNED;
+import static org.mozilla.gecko.Tabs.LOADURL_START_EDITING;
+import static org.mozilla.gecko.Tabs.TabEvents.LOADED;
 import static org.mozilla.gecko.mma.MmaDelegate.NEW_TAB;
+import static org.mozilla.gecko.search.SearchWidgetProvider.INPUT_TYPE_KEY;
 import static org.mozilla.gecko.util.JavaUtil.getBundleSizeInBytes;
 
 public class BrowserApp extends GeckoApp
                         implements ActionModePresenter,
                                    AnchoredPopup.OnVisibilityChangeListener,
                                    BookmarkEditFragment.Callbacks,
                                    BrowserSearch.OnEditSuggestionListener,
                                    BrowserSearch.OnSearchListener,
@@ -866,16 +873,72 @@ public class BrowserApp extends GeckoApp
         }
 
         // We want to get an understanding of how our user base is spread (bug 1221646).
         final String installerPackageName = getPackageManager().getInstallerPackageName(getPackageName());
         Telemetry.sendUIEvent(TelemetryContract.Event.LAUNCH, TelemetryContract.Method.SYSTEM, "installer_" + installerPackageName);
     }
 
     /**
+     * This method is used in order to check if an intent came from {@link SearchWidgetProvider}
+     * and handle it accordingly.
+     * @param intent to be checked and handled
+     * @return True if the intent could be handled
+     */
+    private boolean handleSearchWidgetIntent(Intent intent) {
+        SearchWidgetProvider.InputType input = (SearchWidgetProvider.InputType) (intent == null ?
+                safeStartingIntent.getUnsafe().getSerializableExtra(INPUT_TYPE_KEY) :
+                intent.getSerializableExtra(INPUT_TYPE_KEY));
+
+        if (input == null) {
+            return false;
+        }
+
+        switch (input) {
+            case TEXT:
+                handleTabEditingMode(false);
+                return true;
+            case VOICE:
+                handleTabEditingMode(true);
+                return true;
+            default:
+                // Can't handle this input type, where did it came from though?
+                Log.e(LOGTAG, "can't handle search action :: input == " + input);
+                return false;
+        }
+    }
+
+    private synchronized void handleTabEditingMode(boolean isVoice) {
+        Tab tab = Tabs.getInstance().getLastTabForUrl("about:home");
+
+        if (tab == null) {
+            final Tabs.OnTabsChangedListener tabsChangedListener = new Tabs.OnTabsChangedListener() {
+                @Override
+                public void onTabChanged(Tab tab, TabEvents msg, String data) {
+                    if (tab != null && tab.getURL().equals("about:home") && LOADED.equals(msg)) {
+                        selectTabAndEnterEditingMode(tab.getId(), isVoice);
+                        Tabs.unregisterOnTabsChangedListener(this);
+                    }
+                }
+            };
+            Tabs.registerOnTabsChangedListener(tabsChangedListener);
+        } else {
+            selectTabAndEnterEditingMode(tab.getId(), isVoice);
+        }
+    }
+
+    private void selectTabAndEnterEditingMode(int tabId, boolean isVoice) {
+        Tabs.getInstance().selectTab(tabId);
+        enterEditingMode();
+        if (isVoice) {
+            mBrowserToolbar.launchVoiceRecognizer();
+        }
+    }
+
+    /**
      * Initializes the default Switchboard URLs the first time.
      * @param intent
      */
     private void initSwitchboardAndMma(final Context context, final SafeIntent intent, final boolean isInAutomation) {
         if (isInAutomation) {
             Log.d(LOGTAG, "Switchboard disabled - in automation");
             return;
         } else if (!AppConstants.MOZ_SWITCHBOARD) {
@@ -1703,16 +1766,20 @@ public class BrowserApp extends GeckoApp
                 break;
 
             case "Gecko:DelayedStartup":
                 EventDispatcher.getInstance().unregisterUiThreadListener(this, "Gecko:DelayedStartup");
 
                 // Force tabs panel inflation once the initial pageload is finished.
                 ensureTabsPanelExists();
 
+                if (handleSearchWidgetIntent(safeStartingIntent.getUnsafe())) {
+                    return;
+                }
+
                 if (AppConstants.MOZ_MEDIA_PLAYER) {
                     // Check if the fragment is already added. This should never be true
                     // here, but this is a nice safety check. If casting is disabled,
                     // these classes aren't built. We use reflection to initialize them.
                     final Class<?> mediaManagerClass = getMediaPlayerManager();
 
                     if (mediaManagerClass != null) {
                         try {
@@ -3830,16 +3897,20 @@ public class BrowserApp extends GeckoApp
         if (isViewMultipleAction) {
             openMultipleTabsFromIntent(intent);
         }
 
         for (final BrowserAppDelegate delegate : delegates) {
             delegate.onNewIntent(this, intent);
         }
 
+        if (handleSearchWidgetIntent(externalIntent)) {
+            return;
+        }
+
         if (!mInitialized || !Intent.ACTION_MAIN.equals(action)) {
             return;
         }
 
         // Check to see how many times the app has been launched.
         final String keyName = getPackageName() + ".feedback_launch_count";
 
         // Faster on main thread with an async apply().
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -19,17 +19,16 @@ import android.support.annotation.Nullab
 
 import org.mozilla.gecko.annotation.RobocopTarget;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.distribution.PartnerBrowserCustomizationsClient;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.notifications.WhatsNewReceiver;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.reader.ReaderModeUtils;
-import org.mozilla.gecko.tabs.TabHistoryController;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.JavaUtil;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.webapps.WebAppManifest;
 import org.mozilla.geckoview.GeckoView;
 
@@ -901,16 +900,37 @@ public class Tabs implements BundleEvent
                 return tab;
             }
         }
 
         return null;
     }
 
     /**
+     * Looks for the last open tab with the given URL and private state.
+     * @param url       the URL of the tab we're looking for
+     *
+     * @return last Tab with the given URL, or null if there is no such tab.
+     */
+    public Tab getLastTabForUrl(String url) {
+        if (url == null) {
+            return null;
+        }
+
+        Tab lastTab = null;
+        for (Tab tab : mOrder) {
+            if (url.equals(tab.getURL())) {
+                lastTab = tab;
+            }
+        }
+
+        return lastTab;
+    }
+
+    /**
      * Looks for a reader mode enabled open tab with the given URL and private
      * state.
      *
      * @param url
      *            The URL of the tab we're looking for. The url parameter can be
      *            the actual article URL or the reader mode article URL.
      * @param isPrivate
      *            If true, only look for tabs that are private. If false, only
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbar.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbar.java
@@ -286,16 +286,20 @@ public abstract class BrowserToolbar ext
             public void onClick(View v) {
                 if (activateListener != null) {
                     activateListener.onActivate();
                 }
             }
         });
     }
 
+    public void launchVoiceRecognizer() {
+        urlEditLayout.launchVoiceRecognizer();
+    }
+
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
         prefs.open();
 
         urlDisplayLayout.setOnStopListener(new OnStopListener() {
             @Override
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarEditLayout.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarEditLayout.java
@@ -285,17 +285,17 @@ public class ToolbarEditLayout extends T
         final boolean voiceIsSupported = InputOptionsUtils.supportsVoiceRecognizer(context, prompt);
         if (!voiceIsSupported) {
             return false;
         }
         return GeckoSharedPrefs.forApp(context)
                 .getBoolean(GeckoPreferences.PREFS_VOICE_INPUT_ENABLED, true);
     }
 
-    private void launchVoiceRecognizer() {
+    void launchVoiceRecognizer() {
         Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.ACTIONBAR, "voice_input_launch");
         final Intent intent = InputOptionsUtils.createVoiceRecognizerIntent(getResources().getString(R.string.voicesearch_prompt));
 
         final Activity activity = ActivityUtils.getActivityFromContext(getContext());
         ActivityHandlerHelper.startIntentForActivity(activity, intent, new ActivityResultHandler() {
             @Override
             public void onActivityResult(int resultCode, Intent data) {
                 if (resultCode != Activity.RESULT_OK) {