Commit 9bc77d0a authored by Gang Wu's avatar Gang Wu Committed by Commit Bot

Reland "Reland "Fix image search from search activity""

This is a reland of 3288b2d9

Original change's description:
> Reland "Fix image search from search activity"
> 
> This is a reland of 91eda9e8
> 
> Original change's description:
> > Fix image search from search activity
> > 
> > SearchActivity did not handle image search data since image search need
> > post data, so the fix is add the post data into intent to pass to chrome.
> > 
> > Bug: 1078773
> > Change-Id: I04efdad0bba315508e0b05d3d9e444d8c1a5d955
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2187191
> > Commit-Queue: Gang Wu <gangwu@chromium.org>
> > Reviewed-by: Robert Sesek <rsesek@chromium.org>
> > Reviewed-by: Ender <ender@google.com>
> > Reviewed-by: Ted Choc <tedchoc@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#772335}
> 
> Bug: 1078773
> Change-Id: I86f1fbab1cc27a66ceb5ad8ac987179b8f5e8e68
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2220150
> Reviewed-by: Ted Choc <tedchoc@chromium.org>
> Reviewed-by: Gang Wu <gangwu@chromium.org>
> Commit-Queue: Gang Wu <gangwu@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#774036}

Bug: 1078773
Change-Id: I0084e475067c75db4d89ce3a890694525705c865
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2228983
Commit-Queue: Gang Wu <gangwu@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#775232}
parent 983f291e
...@@ -186,6 +186,17 @@ public class IntentHandler { ...@@ -186,6 +186,17 @@ public class IntentHandler {
*/ */
public static final String EXTRA_INCOGNITO_MODE = "org.chromium.chrome.browser.incognito_mode"; public static final String EXTRA_INCOGNITO_MODE = "org.chromium.chrome.browser.incognito_mode";
/**
* Byte array for the POST data when load a url, only Intents sent by Chrome can use this.
*/
public static final String EXTRA_POST_DATA = "com.android.chrome.post_data";
/**
* The type of the POST data, need to be added to the HTTP request header, only Intents sent by
* Chrome can use this.
*/
public static final String EXTRA_POST_DATA_TYPE = "com.android.chrome.post_data_type";
/** /**
* Fake ComponentName used in constructing TRUSTED_APPLICATION_CODE_EXTRA. * Fake ComponentName used in constructing TRUSTED_APPLICATION_CODE_EXTRA.
*/ */
......
...@@ -46,6 +46,7 @@ import org.chromium.ui.ViewProvider; ...@@ -46,6 +46,7 @@ import org.chromium.ui.ViewProvider;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.modelutil.LazyConstructionPropertyMcp; import org.chromium.ui.modelutil.LazyConstructionPropertyMcp;
import org.chromium.ui.modelutil.MVCListAdapter; import org.chromium.ui.modelutil.MVCListAdapter;
import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -335,6 +336,11 @@ public class AutocompleteCoordinatorImpl implements AutocompleteCoordinator { ...@@ -335,6 +336,11 @@ public class AutocompleteCoordinatorImpl implements AutocompleteCoordinator {
return mMediator; return mMediator;
} }
@VisibleForTesting
ModelList getSuggestionModelList() {
return mMediator.getSuggestionModelList();
}
private void onTileSelected(QueryTile queryTile) { private void onTileSelected(QueryTile queryTile) {
mMediator.onQueryTileSelected(queryTile); mMediator.onQueryTileSelected(queryTile);
} }
......
...@@ -14,6 +14,7 @@ import android.view.LayoutInflater; ...@@ -14,6 +14,7 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.core.app.ActivityOptionsCompat; import androidx.core.app.ActivityOptionsCompat;
...@@ -102,6 +103,8 @@ public class SearchActivity extends AsyncInitializationActivity ...@@ -102,6 +103,8 @@ public class SearchActivity extends AsyncInitializationActivity
/** Input submitted before before the native library was loaded. */ /** Input submitted before before the native library was loaded. */
private String mQueuedUrl; private String mQueuedUrl;
private String mQueuedPostDataType;
private byte[] mQueuedPostData;
/** The View that represents the search box. */ /** The View that represents the search box. */
private SearchActivityLocationBarLayout mSearchBox; private SearchActivityLocationBarLayout mSearchBox;
...@@ -273,7 +276,7 @@ public class SearchActivity extends AsyncInitializationActivity ...@@ -273,7 +276,7 @@ public class SearchActivity extends AsyncInitializationActivity
assert !mIsActivityUsable assert !mIsActivityUsable
: "finishDeferredInitialization() incorrectly called multiple times"; : "finishDeferredInitialization() incorrectly called multiple times";
mIsActivityUsable = true; mIsActivityUsable = true;
if (mQueuedUrl != null) loadUrl(mQueuedUrl); if (mQueuedUrl != null) loadUrl(mQueuedUrl, mQueuedPostDataType, mQueuedPostData);
// TODO(tedchoc): Warmup triggers the CustomTab layout to be inflated, but this widget // TODO(tedchoc): Warmup triggers the CustomTab layout to be inflated, but this widget
// will navigate to Tabbed mode. Investigate whether this can inflate // will navigate to Tabbed mode. Investigate whether this can inflate
...@@ -327,29 +330,53 @@ public class SearchActivity extends AsyncInitializationActivity ...@@ -327,29 +330,53 @@ public class SearchActivity extends AsyncInitializationActivity
} }
@Override @Override
public void loadUrl(String url) { public void loadUrl(String url, @Nullable String postDataType, @Nullable byte[] postData) {
// Wait until native has loaded. // Wait until native has loaded.
if (!mIsActivityUsable) { if (!mIsActivityUsable) {
mQueuedUrl = url; mQueuedUrl = url;
mQueuedPostDataType = postDataType;
mQueuedPostData = postData;
return; return;
} }
Intent intent = createIntentForStartActivity(url, postDataType, postData);
if (intent == null) return;
IntentUtils.safeStartActivity(this, intent,
ActivityOptionsCompat
.makeCustomAnimation(this, android.R.anim.fade_in, android.R.anim.fade_out)
.toBundle());
RecordUserAction.record("SearchWidget.SearchMade");
finish();
}
/**
* Creates an intent that will be used to launch Chrome.
*
* @param url The URL to be loaded.
* @param postDataType postData type.
* @param postData Post-data to include in the tab URL's request body, ex. bitmap when
* image search.
* @return the intent will be passed to ChromeLauncherActivity, null if input was emprty.
*/
private Intent createIntentForStartActivity(
String url, @Nullable String postDataType, @Nullable byte[] postData) {
// Don't do anything if the input was empty. This is done after the native check to prevent // Don't do anything if the input was empty. This is done after the native check to prevent
// resending a queued query after the user deleted it. // resending a queued query after the user deleted it.
if (TextUtils.isEmpty(url)) return; if (TextUtils.isEmpty(url)) return null;
// Fix up the URL and send it to the full browser. // Fix up the URL and send it to the full browser.
GURL fixedUrl = UrlFormatter.fixupUrl(url); GURL fixedUrl = UrlFormatter.fixupUrl(url);
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(fixedUrl.getValidSpecOrEmpty())); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(fixedUrl.getValidSpecOrEmpty()));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
intent.setClass(this, ChromeLauncherActivity.class); intent.setClass(this, ChromeLauncherActivity.class);
if (!TextUtils.isEmpty(postDataType) && postData != null && postData.length != 0) {
intent.putExtra(IntentHandler.EXTRA_POST_DATA_TYPE, postDataType);
intent.putExtra(IntentHandler.EXTRA_POST_DATA, postData);
}
IntentHandler.addTrustedIntentExtras(intent); IntentHandler.addTrustedIntentExtras(intent);
IntentUtils.safeStartActivity(this, intent,
ActivityOptionsCompat return intent;
.makeCustomAnimation(this, android.R.anim.fade_in, android.R.anim.fade_out)
.toBundle());
RecordUserAction.record("SearchWidget.SearchMade");
finish();
} }
private ViewGroup createContentView() { private ViewGroup createContentView() {
......
...@@ -28,7 +28,7 @@ public class SearchActivityLocationBarLayout extends LocationBarLayout { ...@@ -28,7 +28,7 @@ public class SearchActivityLocationBarLayout extends LocationBarLayout {
/** Delegates calls out to the containing Activity. */ /** Delegates calls out to the containing Activity. */
public static interface Delegate { public static interface Delegate {
/** Load a URL in the associated tab. */ /** Load a URL in the associated tab. */
void loadUrl(String url); void loadUrl(String url, @Nullable String postDataType, @Nullable byte[] postData);
/** The user hit the back button. */ /** The user hit the back button. */
void backKeyPressed(); void backKeyPressed();
...@@ -56,8 +56,9 @@ public class SearchActivityLocationBarLayout extends LocationBarLayout { ...@@ -56,8 +56,9 @@ public class SearchActivityLocationBarLayout extends LocationBarLayout {
} }
@Override @Override
public void loadUrl(String url, int transition, long inputStart) { public void loadUrlWithPostData(String url, int transition, long inputStart,
mDelegate.loadUrl(url); @Nullable String postDataType, @Nullable byte[] postData) {
mDelegate.loadUrl(url, postDataType, postData);
LocaleManager.getInstance().recordLocaleBasedSearchMetrics(true, url, transition); LocaleManager.getInstance().recordLocaleBasedSearchMetrics(true, url, transition);
} }
......
...@@ -33,6 +33,7 @@ import org.chromium.components.url_formatter.UrlFormatter; ...@@ -33,6 +33,7 @@ import org.chromium.components.url_formatter.UrlFormatter;
import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.common.Referrer; import org.chromium.content_public.common.Referrer;
import org.chromium.content_public.common.ResourceRequestBody;
import org.chromium.ui.base.PageTransition; import org.chromium.ui.base.PageTransition;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
import org.chromium.url.GURL; import org.chromium.url.GURL;
...@@ -298,8 +299,9 @@ public class ChromeTabCreator extends TabCreatorManager.TabCreator { ...@@ -298,8 +299,9 @@ public class ChromeTabCreator extends TabCreatorManager.TabCreator {
* @param intentTimestamp the time the intent was received. * @param intentTimestamp the time the intent was received.
* @return the tab the URL was opened in, could be a new tab or a reused one. * @return the tab the URL was opened in, could be a new tab or a reused one.
*/ */
public Tab launchUrlFromExternalApp(String url, String referer, String headers, // TODO(crbug.com/1081924): Clean up the launches from SearchActivity/Chrome.
String appId, boolean forceNewTab, Intent intent, long intentTimestamp) { public Tab launchUrlFromExternalApp(String url, String referer, String headers, String appId,
boolean forceNewTab, Intent intent, long intentTimestamp) {
assert !mIncognito; assert !mIncognito;
boolean isLaunchedFromChrome = TextUtils.equals(appId, mActivity.getPackageName()); boolean isLaunchedFromChrome = TextUtils.equals(appId, mActivity.getPackageName());
...@@ -309,11 +311,30 @@ public class ChromeTabCreator extends TabCreatorManager.TabCreator { ...@@ -309,11 +311,30 @@ public class ChromeTabCreator extends TabCreatorManager.TabCreator {
// reused either. // reused either.
LoadUrlParams loadUrlParams = new LoadUrlParams(url); LoadUrlParams loadUrlParams = new LoadUrlParams(url);
loadUrlParams.setIntentReceivedTimestamp(intentTimestamp); loadUrlParams.setIntentReceivedTimestamp(intentTimestamp);
loadUrlParams.setVerbatimHeaders(headers);
if (referer != null) { if (referer != null) {
loadUrlParams.setReferrer( loadUrlParams.setReferrer(
new Referrer(referer, IntentHandler.getReferrerPolicyFromIntent(intent))); new Referrer(referer, IntentHandler.getReferrerPolicyFromIntent(intent)));
} }
// Handle post data case.
if (IntentHandler.wasIntentSenderChrome(intent)) {
String postDataType =
IntentUtils.safeGetStringExtra(intent, IntentHandler.EXTRA_POST_DATA_TYPE);
byte[] postData =
IntentUtils.safeGetByteArrayExtra(intent, IntentHandler.EXTRA_POST_DATA);
if (!TextUtils.isEmpty(postDataType) && postData != null && postData.length != 0) {
StringBuilder appendToHeader = new StringBuilder();
appendToHeader.append("Content-Type: ");
appendToHeader.append(postDataType);
if (TextUtils.isEmpty(headers)) {
headers = appendToHeader.toString();
} else {
headers = headers + "\r\n" + appendToHeader.toString();
}
loadUrlParams.setPostData(ResourceRequestBody.createFromBytes(postData));
}
}
loadUrlParams.setVerbatimHeaders(headers);
return createNewTab(loadUrlParams, TabLaunchType.FROM_EXTERNAL_APP, null, intent); return createNewTab(loadUrlParams, TabLaunchType.FROM_EXTERNAL_APP, null, intent);
} }
......
...@@ -2,6 +2,7 @@ include_rules = [ ...@@ -2,6 +2,7 @@ include_rules = [
"+chrome/app", "+chrome/app",
"+chrome/browser/android/lifecycle", "+chrome/browser/android/lifecycle",
"+chrome/browser/profiles/android/java", "+chrome/browser/profiles/android/java",
"+chrome/browser/share/android/java",
"+chrome/browser/tab/java", "+chrome/browser/tab/java",
"+chrome/browser/thumbnail/generator/android/java", "+chrome/browser/thumbnail/generator/android/java",
"+chrome/browser/ui/android/appmenu", "+chrome/browser/ui/android/appmenu",
......
...@@ -330,6 +330,8 @@ public abstract class ChromeFeatureList { ...@@ -330,6 +330,8 @@ public abstract class ChromeFeatureList {
public static final String OMNIBOX_ASSISTANT_VOICE_SEARCH = "OmniboxAssistantVoiceSearch"; public static final String OMNIBOX_ASSISTANT_VOICE_SEARCH = "OmniboxAssistantVoiceSearch";
public static final String OMNIBOX_COMPACT_SUGGESTIONS = "OmniboxCompactSuggestions"; public static final String OMNIBOX_COMPACT_SUGGESTIONS = "OmniboxCompactSuggestions";
public static final String OMNIBOX_DEFERRED_KEYBOARD_POPUP = "OmniboxDeferredKeyboardPopup"; public static final String OMNIBOX_DEFERRED_KEYBOARD_POPUP = "OmniboxDeferredKeyboardPopup";
public static final String OMNIBOX_ENABLE_CLIPBOARD_PROVIDER_IMAGE_SUGGESTIONS =
"OmniboxEnableClipboardProviderImageSuggestions";
public static final String OMNIBOX_HIDE_SCHEME_IN_STEADY_STATE = public static final String OMNIBOX_HIDE_SCHEME_IN_STEADY_STATE =
"OmniboxUIExperimentHideSteadyStateUrlScheme"; "OmniboxUIExperimentHideSteadyStateUrlScheme";
public static final String OMNIBOX_HIDE_TRIVIAL_SUBDOMAINS_IN_STEADY_STATE = public static final String OMNIBOX_HIDE_TRIVIAL_SUBDOMAINS_IN_STEADY_STATE =
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
package org.chromium.chrome.browser.omnibox.suggestions; package org.chromium.chrome.browser.omnibox.suggestions;
import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteMediator.DropdownItemViewInfo;
import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
/** /**
* Utility methods providing access to package-private methods in {@link AutocompleteCoordinator} * Utility methods providing access to package-private methods in {@link AutocompleteCoordinator}
* for tests. * for tests.
...@@ -34,4 +37,28 @@ public class AutocompleteCoordinatorTestUtils { ...@@ -34,4 +37,28 @@ public class AutocompleteCoordinatorTestUtils {
AutocompleteCoordinator coordinator) { AutocompleteCoordinator coordinator) {
return ((AutocompleteCoordinatorImpl) coordinator).getSuggestionsDropdown(); return ((AutocompleteCoordinatorImpl) coordinator).getSuggestionsDropdown();
} }
/**
* @return The {@link OmniboxSuggestion} at the specified index.
*/
public static OmniboxSuggestion getOmniboxSuggestionAt(
AutocompleteCoordinator coordinator, int index) {
return coordinator.getSuggestionAt(index);
}
/**
* @return The index of the first suggestion which is |type|.
*/
public static int getIndexForFirstSuggestionOfType(
AutocompleteCoordinator coordinator, @OmniboxSuggestionUiType int type) {
ModelList currentModels =
((AutocompleteCoordinatorImpl) coordinator).getSuggestionModelList();
for (int i = 0; i < currentModels.size(); i++) {
DropdownItemViewInfo info = (DropdownItemViewInfo) currentModels.get(i);
if (info.type == type) {
return i;
}
}
return -1;
}
} }
include_rules = [ include_rules = [
"+components/dom_distiller/core/android", "+components/dom_distiller/core/android",
"+content/public/android", "+content/public/android",
"+content/public/test/android/javatests",
"+ui/android", "+ui/android",
] ]
...@@ -20,6 +20,7 @@ import android.provider.MediaStore; ...@@ -20,6 +20,7 @@ import android.provider.MediaStore;
import android.support.test.filters.SmallTest; import android.support.test.filters.SmallTest;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import androidx.core.util.ObjectsCompat;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
...@@ -34,6 +35,8 @@ import org.chromium.base.test.BaseJUnit4ClassRunner; ...@@ -34,6 +35,8 @@ import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.CallbackHelper; import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.DisableIf; import org.chromium.base.test.util.DisableIf;
import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.DisabledTest;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.ui.base.Clipboard; import org.chromium.ui.base.Clipboard;
import org.chromium.ui.test.util.DummyUiActivityTestCase; import org.chromium.ui.test.util.DummyUiActivityTestCase;
...@@ -132,6 +135,13 @@ public class ShareImageFileUtilsTest extends DummyUiActivityTestCase { ...@@ -132,6 +135,13 @@ public class ShareImageFileUtilsTest extends DummyUiActivityTestCase {
getActivity(), TEST_IMAGE_DATA, fileExtension, imageCallback); getActivity(), TEST_IMAGE_DATA, fileExtension, imageCallback);
imageCallback.waitForCallback(0, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS); imageCallback.waitForCallback(0, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
Clipboard.getInstance().setImageUri(imageCallback.getImageUri()); Clipboard.getInstance().setImageUri(imageCallback.getImageUri());
CriteriaHelper.pollInstrumentationThread(new Criteria() {
@Override
public boolean isSatisfied() {
return ObjectsCompat.equals(
Clipboard.getInstance().getImageUri(), imageCallback.getImageUri());
}
});
return imageCallback.getImageUri(); return imageCallback.getImageUri();
} }
......
...@@ -36,6 +36,7 @@ import org.chromium.base.annotations.JNINamespace; ...@@ -36,6 +36,7 @@ import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods; import org.chromium.base.annotations.NativeMethods;
import org.chromium.base.compat.ApiHelperForO; import org.chromium.base.compat.ApiHelperForO;
import org.chromium.base.metrics.RecordUserAction; import org.chromium.base.metrics.RecordUserAction;
import org.chromium.base.task.AsyncTask;
import org.chromium.ui.R; import org.chromium.ui.R;
import org.chromium.ui.widget.Toast; import org.chromium.ui.widget.Toast;
...@@ -277,6 +278,8 @@ public class Clipboard implements ClipboardManager.OnPrimaryClipChangedListener ...@@ -277,6 +278,8 @@ public class Clipboard implements ClipboardManager.OnPrimaryClipChangedListener
/** /**
* Setting the clipboard's current primary clip to an image. * Setting the clipboard's current primary clip to an image.
* This method requires background work and might not be immediately committed upon returning
* from this method.
* @param Uri The {@link Uri} will become the content of the clipboard's primary clip. * @param Uri The {@link Uri} will become the content of the clipboard's primary clip.
*/ */
public void setImageUri(final Uri uri) { public void setImageUri(final Uri uri) {
...@@ -287,9 +290,19 @@ public class Clipboard implements ClipboardManager.OnPrimaryClipChangedListener ...@@ -287,9 +290,19 @@ public class Clipboard implements ClipboardManager.OnPrimaryClipChangedListener
grantUriPermission(uri); grantUriPermission(uri);
ClipData clip = ClipData.newUri( // ClipData.newUri may access the disk (for reading mime types), and cause
ContextUtils.getApplicationContext().getContentResolver(), "image", uri); // StrictModeDiskReadViolation if do it on UI thread.
setPrimaryClipNoException(clip); new AsyncTask<ClipData>() {
@Override
protected ClipData doInBackground() {
return ClipData.newUri(
ContextUtils.getApplicationContext().getContentResolver(), "image", uri);
}
@Override
protected void onPostExecute(ClipData clipData) {
setPrimaryClipNoException(clipData);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment