Bug 1320072 - Support direct search from intent, r=sebastian
authormaliu <max@mxli.us>
Wed, 30 Nov 2016 20:25:32 +0800
changeset 462750 d24c7655825cfa383c17ddf74afe4a6e0b3787a5
parent 462749 bb0fc7b8fd043e916a8e31bae021baad9f179f70
child 462751 ec799efea60cbc4462ef8cc155b6bdb2a9789e91
push id41857
push userbmo:mh+mozilla@glandium.org
push dateWed, 18 Jan 2017 00:24:11 +0000
reviewerssebastian
bugs1320072
milestone53.0a1
Bug 1320072 - Support direct search from intent, r=sebastian MozReview-Commit-ID: GAoC4jKVyl7
mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/SafeIntent.java
mobile/android/search/java/org/mozilla/search/SearchActivity.java
mobile/android/search/manifests/SearchAndroidManifest_activities.xml.in
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/SafeIntent.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/SafeIntent.java
@@ -5,16 +5,17 @@
  */
 
 // This should be in util/, but is here because of build dependency issues.
 package org.mozilla.gecko.mozglue;
 
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.annotation.Nullable;
 import android.util.Log;
 
 import java.util.ArrayList;
 
 /**
  * External applications can pass values into Intents that can cause us to crash: in defense,
  * we wrap {@link Intent} and catch the exceptions they may force us to throw. See bug 1090385
  * for more.
@@ -71,16 +72,29 @@ public class SafeIntent {
             Log.w(LOGTAG, "Couldn't get intent extras: OOM. Malformed?");
             return null;
         } catch (RuntimeException e) {
             Log.w(LOGTAG, "Couldn't get intent extras.", e);
             return null;
         }
     }
 
+    @Nullable
+    public Bundle getExtras() {
+        try {
+            return intent.getExtras();
+        } catch (OutOfMemoryError e) {
+            Log.w(LOGTAG, "Couldn't get intent extras: OOM. Malformed?");
+            return null;
+        } catch (RuntimeException e) {
+            Log.w(LOGTAG, "Couldn't get intent extras.", e);
+            return null;
+        }
+    }
+
     public Bundle getBundleExtra(final String name) {
         try {
             return intent.getBundleExtra(name);
         } catch (OutOfMemoryError e) {
             Log.w(LOGTAG, "Couldn't get intent extras: OOM. Malformed?");
             return null;
         } catch (RuntimeException e) {
             Log.w(LOGTAG, "Couldn't get intent extras.", e);
--- a/mobile/android/search/java/org/mozilla/search/SearchActivity.java
+++ b/mobile/android/search/java/org/mozilla/search/SearchActivity.java
@@ -1,34 +1,37 @@
 /* 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.search;
 
+import android.app.SearchManager;
 import android.support.annotation.NonNull;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.Locales;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserContract.SearchHistory;
 import org.mozilla.gecko.distribution.Distribution;
+import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.search.SearchEngine;
 import org.mozilla.gecko.search.SearchEngineManager;
 import org.mozilla.gecko.search.SearchEngineManager.SearchEngineCallback;
 import org.mozilla.search.autocomplete.SearchBar;
 import org.mozilla.search.autocomplete.SuggestionsFragment;
 
 import android.content.AsyncQueryHandler;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.support.annotation.Nullable;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.Interpolator;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
@@ -42,17 +45,18 @@ import android.animation.ObjectAnimator;
  */
 public class SearchActivity extends Locales.LocaleAwareFragmentActivity
         implements AcceptsSearchQuery, SearchEngineCallback {
 
     private static final String LOGTAG = "GeckoSearchActivity";
 
     private static final String KEY_SEARCH_STATE = "search_state";
     private static final String KEY_EDIT_STATE = "edit_state";
-    private static final String KEY_QUERY = "query";
+    private static final String KEY_QUERY = SearchManager.QUERY;
+    private static final String KEY_INCOMING_QUERY = "incoming_query";
 
     static enum SearchState {
         PRESEARCH,
         POSTSEARCH
     }
 
     static enum EditState {
         WAITING,
@@ -169,34 +173,68 @@ public class SearchActivity extends Loca
 
         suggestions = findViewById(R.id.suggestions);
 
         animationCard = findViewById(R.id.animation_card);
 
         cardPaddingX = getResources().getDimensionPixelSize(R.dimen.search_row_padding);
         cardPaddingY = getResources().getDimensionPixelSize(R.dimen.search_row_padding);
 
-        if (savedInstanceState != null) {
+        final String query;
+
+        final String incomingQuery = extractQuery(getIntent());
+        final String previousInstanceQuery = extractQuery(savedInstanceState);
+        final String previousIncomingQuery = savedInstanceState == null ? null : savedInstanceState.getString(KEY_INCOMING_QUERY);
+
+        if (savedInstanceState != null && (TextUtils.isEmpty(incomingQuery) || incomingQuery.equals(previousIncomingQuery))) {
             setSearchState(SearchState.valueOf(savedInstanceState.getString(KEY_SEARCH_STATE)));
             setEditState(EditState.valueOf(savedInstanceState.getString(KEY_EDIT_STATE)));
+            query = previousInstanceQuery;
+        } else {
+            query = incomingQuery;
+            if (!TextUtils.isEmpty(query)) {
+                setSearchState(SearchState.POSTSEARCH);
+            }
+        }
 
-            final String query = savedInstanceState.getString(KEY_QUERY);
+        if (!TextUtils.isEmpty(query)) {
             searchBar.setText(query);
-
             // If we're in the postsearch state, we need to re-do the query.
             if (searchState == SearchState.POSTSEARCH) {
-                startSearch(query);
+                onSearch(query);
             }
         } else {
             // If there isn't a state to restore, the activity will start in the presearch state,
             // and we should enter editing mode to bring up the keyboard.
             setEditState(EditState.EDITING);
         }
     }
 
+    @Nullable
+    private String extractQuery(Intent intent) {
+        return extractQuery(intent == null ? null : new SafeIntent(intent).getExtras());
+    }
+
+    @Nullable
+    private String extractQuery(Bundle bundle) {
+        if (bundle == null) {
+            return null;
+        }
+
+        String queryString = (String) bundle.getCharSequence("android.intent.extra.PROCESS_TEXT_READONLY");;
+        if (TextUtils.isEmpty(queryString)) {
+            queryString = (String) bundle.getCharSequence("android.intent.extra.PROCESS_TEXT");
+        }
+        if (TextUtils.isEmpty(queryString)) {
+            queryString = bundle.getString(KEY_QUERY);
+        }
+
+        return queryString;
+    }
+
     @Override
     protected void onDestroy() {
         super.onDestroy();
         searchEngineManager.unregisterListeners();
         engine = null;
         suggestionsFragment = null;
         postSearchFragment = null;
         queryHandler = null;
@@ -220,26 +258,34 @@ public class SearchActivity extends Loca
         Telemetry.stopUISession(TelemetryContract.Session.SEARCH_ACTIVITY);
     }
 
     @Override
     public void onNewIntent(Intent intent) {
         // Reset the activity in the presearch state if it was launched from a new intent.
         setSearchState(SearchState.PRESEARCH);
 
-        // Enter editing mode and reset the query. We must reset the query after entering
-        // edit mode in order for the suggestions to update.
-        setEditState(EditState.EDITING);
-        searchBar.setText("");
+        final String queryString = extractQuery(intent);
+        if(!TextUtils.isEmpty(queryString)) {
+            setSearchState(SearchState.POSTSEARCH);
+            searchBar.setText(queryString);
+            onSearch(queryString);
+        } else {
+            // Enter editing mode and reset the query. We must reset the query after entering
+            // edit mode in order for the suggestions to update.
+            setEditState(EditState.EDITING);
+            searchBar.setText("");
+        }
     }
 
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
 
+        outState.putString(KEY_INCOMING_QUERY, extractQuery(getIntent()));
         outState.putString(KEY_SEARCH_STATE, searchState.toString());
         outState.putString(KEY_EDIT_STATE, editState.toString());
         outState.putString(KEY_QUERY, searchBar.getText());
     }
 
     @Override
     public void onSuggest(String query) {
         searchBar.setText(query);
--- a/mobile/android/search/manifests/SearchAndroidManifest_activities.xml.in
+++ b/mobile/android/search/manifests/SearchAndroidManifest_activities.xml.in
@@ -4,20 +4,29 @@
             android:launchMode="singleTop"
             android:taskAffinity="@ANDROID_PACKAGE_NAME@.SEARCH"
             android:icon="@drawable/search_launcher"
             android:label="@string/search_app_name"
             android:configChanges="orientation|screenSize"
             android:theme="@style/AppTheme">
             <intent-filter>
                 <action android:name="android.intent.action.ASSIST"/>
+                <action android:name="android.intent.action.SEARCH"/>
+                <action android:name="android.intent.action.SEARCH_LONG_PRESS"/>
+                <action android:name="android.intent.action.WEB_SEARCH"/>
+                <action android:name="com.google.android.gms.actions.SEARCH_ACTION"/>
 
                 <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
 
+            <intent-filter>
+                <action android:name="android.intent.action.PROCESS_TEXT"/>
+                <data android:mimeType="text/plain"/>
+            </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