Merge change 8186 into donut
* changes: Move global search intent launching to search dialog
This commit is contained in:
@ -34,7 +34,10 @@ import android.graphics.drawable.Animatable;
|
|||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.RemoteException;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.provider.Browser;
|
||||||
import android.server.search.SearchableInfo;
|
import android.server.search.SearchableInfo;
|
||||||
import android.speech.RecognizerIntent;
|
import android.speech.RecognizerIntent;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
@ -42,6 +45,7 @@ import android.text.InputType;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.text.util.Regex;
|
import android.text.util.Regex;
|
||||||
|
import android.util.AndroidRuntimeException;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.ContextThemeWrapper;
|
import android.view.ContextThemeWrapper;
|
||||||
@ -1093,7 +1097,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
*/
|
*/
|
||||||
protected void launchQuerySearch(int actionKey, String actionMsg) {
|
protected void launchQuerySearch(int actionKey, String actionMsg) {
|
||||||
String query = mSearchAutoComplete.getText().toString();
|
String query = mSearchAutoComplete.getText().toString();
|
||||||
Intent intent = createIntent(Intent.ACTION_SEARCH, null, null, query, null,
|
String action = mGlobalSearchMode ? Intent.ACTION_WEB_SEARCH : Intent.ACTION_SEARCH;
|
||||||
|
Intent intent = createIntent(action, null, null, query, null,
|
||||||
actionKey, actionMsg);
|
actionKey, actionMsg);
|
||||||
launchIntent(intent);
|
launchIntent(intent);
|
||||||
}
|
}
|
||||||
@ -1245,14 +1250,125 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.d(LOG_TAG, "launching " + intent);
|
Log.d(LOG_TAG, "launching " + intent);
|
||||||
|
try {
|
||||||
|
// in global search mode, we send the activity straight to the original suggestion
|
||||||
|
// source. this is because GlobalSearch may not have permission to launch the
|
||||||
|
// intent, and to avoid the extra step of going through GlobalSearch.
|
||||||
|
if (mGlobalSearchMode) {
|
||||||
|
launchGlobalSearchIntent(intent);
|
||||||
|
} else {
|
||||||
getContext().startActivity(intent);
|
getContext().startActivity(intent);
|
||||||
|
// in global search mode, SearchDialogWrapper#performActivityResuming
|
||||||
// in global search mode, SearchDialogWrapper#performActivityResuming will handle hiding
|
// will handle hiding the dialog when the next activity starts, but for
|
||||||
// the dialog when the next activity starts, but for in-app search, we still need to
|
// in-app search, we still need to dismiss the dialog.
|
||||||
// dismiss the dialog.
|
|
||||||
if (!mGlobalSearchMode) {
|
|
||||||
dismiss();
|
dismiss();
|
||||||
}
|
}
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
Log.e(LOG_TAG, "Failed launch activity: " + intent, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void launchGlobalSearchIntent(Intent intent) {
|
||||||
|
final String packageName;
|
||||||
|
// GlobalSearch puts the original source of the suggestion in the
|
||||||
|
// 'component name' column. If set, we send the intent to that activity.
|
||||||
|
// We trust GlobalSearch to always set this to the suggestion source.
|
||||||
|
String intentComponent = intent.getStringExtra(SearchManager.COMPONENT_NAME_KEY);
|
||||||
|
if (intentComponent != null) {
|
||||||
|
ComponentName componentName = ComponentName.unflattenFromString(intentComponent);
|
||||||
|
intent.setComponent(componentName);
|
||||||
|
intent.removeExtra(SearchManager.COMPONENT_NAME_KEY);
|
||||||
|
// Launch the intent as the suggestion source.
|
||||||
|
// This prevents sources from using the search dialog to launch
|
||||||
|
// intents that they don't have permission for themselves.
|
||||||
|
packageName = componentName.getPackageName();
|
||||||
|
} else {
|
||||||
|
// If there is no component in the suggestion, it must be a built-in suggestion
|
||||||
|
// from GlobalSearch (e.g. "Search the web for") or the intent
|
||||||
|
// launched when pressing the search/go button in the search dialog.
|
||||||
|
// Launch the intent with the permissions of GlobalSearch.
|
||||||
|
packageName = mSearchable.getSearchActivity().getPackageName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Launch all global search suggestions as new tasks, since they don't relate
|
||||||
|
// to the current task.
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
setBrowserApplicationId(intent);
|
||||||
|
|
||||||
|
if (DBG) Log.d(LOG_TAG, "Launching intent " + intent.toURI() + " as " + packageName);
|
||||||
|
startActivityInPackage(intent, packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the intent is to open an HTTP or HTTPS URL, we set
|
||||||
|
* {@link Browser#EXTRA_APPLICATION_ID} so that any existing browser window that
|
||||||
|
* has been opened by us for the same URL will be reused.
|
||||||
|
*/
|
||||||
|
private void setBrowserApplicationId(Intent intent) {
|
||||||
|
Uri data = intent.getData();
|
||||||
|
if (Intent.ACTION_VIEW.equals(intent.getAction()) && data != null) {
|
||||||
|
String scheme = data.getScheme();
|
||||||
|
if (scheme != null && scheme.startsWith("http")) {
|
||||||
|
intent.putExtra(Browser.EXTRA_APPLICATION_ID, data.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an activity as if it had been started by the given package.
|
||||||
|
*
|
||||||
|
* @param intent The description of the activity to start.
|
||||||
|
* @param packageName
|
||||||
|
* @throws ActivityNotFoundException If the intent could not be resolved to
|
||||||
|
* and existing activity.
|
||||||
|
* @throws SecurityException If the package does not have permission to start
|
||||||
|
* start the activity.
|
||||||
|
* @throws AndroidRuntimeException If some other error occurs.
|
||||||
|
*/
|
||||||
|
private void startActivityInPackage(Intent intent, String packageName) {
|
||||||
|
try {
|
||||||
|
int uid = ActivityThread.getPackageManager().getPackageUid(packageName);
|
||||||
|
if (uid < 0) {
|
||||||
|
throw new AndroidRuntimeException("Package UID not found " + packageName);
|
||||||
|
}
|
||||||
|
String resolvedType = intent.resolveTypeIfNeeded(getContext().getContentResolver());
|
||||||
|
IBinder resultTo = null;
|
||||||
|
String resultWho = null;
|
||||||
|
int requestCode = -1;
|
||||||
|
boolean onlyIfNeeded = false;
|
||||||
|
int result = ActivityManagerNative.getDefault().startActivityInPackage(
|
||||||
|
uid, intent, resolvedType, resultTo, resultWho, requestCode, onlyIfNeeded);
|
||||||
|
checkStartActivityResult(result, intent);
|
||||||
|
} catch (RemoteException ex) {
|
||||||
|
throw new AndroidRuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stolen from Instrumentation.checkStartActivityResult()
|
||||||
|
private static void checkStartActivityResult(int res, Intent intent) {
|
||||||
|
if (res >= IActivityManager.START_SUCCESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (res) {
|
||||||
|
case IActivityManager.START_INTENT_NOT_RESOLVED:
|
||||||
|
case IActivityManager.START_CLASS_NOT_FOUND:
|
||||||
|
if (intent.getComponent() != null)
|
||||||
|
throw new ActivityNotFoundException(
|
||||||
|
"Unable to find explicit activity class "
|
||||||
|
+ intent.getComponent().toShortString()
|
||||||
|
+ "; have you declared this activity in your AndroidManifest.xml?");
|
||||||
|
throw new ActivityNotFoundException(
|
||||||
|
"No Activity found to handle " + intent);
|
||||||
|
case IActivityManager.START_PERMISSION_DENIED:
|
||||||
|
throw new SecurityException("Not allowed to start activity "
|
||||||
|
+ intent);
|
||||||
|
case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
|
||||||
|
throw new AndroidRuntimeException(
|
||||||
|
"FORWARD_RESULT_FLAG used while also requesting a result");
|
||||||
|
default:
|
||||||
|
throw new AndroidRuntimeException("Unknown error code "
|
||||||
|
+ res + " when starting " + intent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1460,8 +1576,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
|||||||
intent.putExtra(SearchManager.ACTION_KEY, actionKey);
|
intent.putExtra(SearchManager.ACTION_KEY, actionKey);
|
||||||
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
|
intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
|
||||||
}
|
}
|
||||||
// attempt to enforce security requirement (no 3rd-party intents)
|
// Only allow 3rd-party intents from GlobalSearch
|
||||||
|
if (!mGlobalSearchMode) {
|
||||||
intent.setComponent(mSearchable.getSearchActivity());
|
intent.setComponent(mSearchable.getSearchActivity());
|
||||||
|
}
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user