Skip to content

Commit

Permalink
Fix 6667238: allow market apps to support ACTION_ASSIST
Browse files Browse the repository at this point in the history
This change allows market apps and 3rd parties to supply an activity
that responds to ACTION_ASSIST (e.g. market apps).

It also adds a test app to respond to the ASSIST intent and force
the intent disambiguation dialog to appear.

Change-Id: I5a78863c6a9546d18c66275187d178f6a1c9ee17
  • Loading branch information
Jim Miller committed Jun 19, 2012
1 parent c258546 commit 45308b1
Show file tree
Hide file tree
Showing 17 changed files with 254 additions and 116 deletions.
26 changes: 7 additions & 19 deletions core/java/android/app/SearchManager.java
Expand Up @@ -840,29 +840,17 @@ public List<SearchableInfo> getSearchablesInGlobalSearch() {
}

/**
* Returns true if the global assist activity is available.
* @return True if the assistant is available.
*
* @hide
*/
public final boolean isAssistantAvailable() {
Intent intent = getAssistIntent();
return intent != null
&& mContext.getPackageManager().queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
}

/**
* Gets an intent to launch the global assist activity, or null if not available.
* Gets an intent for launching installed assistant activity, or null if not available.
* @return The assist intent.
*
* @hide
*/
public final Intent getAssistIntent() {
ComponentName globalSearchActivity = getGlobalSearchActivity();
if (globalSearchActivity != null) {
Intent intent = new Intent(Intent.ACTION_ASSIST);
intent.setPackage(globalSearchActivity.getPackageName());
public static final Intent getAssistIntent(Context context) {
PackageManager pm = context.getPackageManager();
Intent intent = new Intent(Intent.ACTION_ASSIST);
ComponentName component = intent.resolveActivity(pm);
if (component != null) {
intent.setComponent(component);
return intent;
}
return null;
Expand Down
Expand Up @@ -29,7 +29,6 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Vibrator;
Expand Down Expand Up @@ -1209,25 +1208,32 @@ public boolean replaceTargetDrawablesIfPresent(ComponentName component, String n
int existingResId) {
if (existingResId == 0) return false;

try {
PackageManager packageManager = mContext.getPackageManager();
// Look for the search icon specified in the activity meta-data
Bundle metaData = packageManager.getActivityInfo(
component, PackageManager.GET_META_DATA).metaData;
if (metaData != null) {
int iconResId = metaData.getInt(name);
if (iconResId != 0) {
Resources res = packageManager.getResourcesForActivity(component);
return replaceTargetDrawables(res, existingResId, iconResId);
boolean replaced = false;
if (component != null) {
try {
PackageManager packageManager = mContext.getPackageManager();
// Look for the search icon specified in the activity meta-data
Bundle metaData = packageManager.getActivityInfo(
component, PackageManager.GET_META_DATA).metaData;
if (metaData != null) {
int iconResId = metaData.getInt(name);
if (iconResId != 0) {
Resources res = packageManager.getResourcesForActivity(component);
replaced = replaceTargetDrawables(res, existingResId, iconResId);
}
}
} catch (NameNotFoundException e) {
Log.w(TAG, "Failed to swap drawable; "
+ component.flattenToShortString() + " not found", e);
} catch (Resources.NotFoundException nfe) {
Log.w(TAG, "Failed to swap drawable from "
+ component.flattenToShortString(), nfe);
}
} catch (NameNotFoundException e) {
Log.w(TAG, "Failed to swap drawable; "
+ component.flattenToShortString() + " not found", e);
} catch (Resources.NotFoundException nfe) {
Log.w(TAG, "Failed to swap drawable from "
+ component.flattenToShortString(), nfe);
}
return false;
if (!replaced) {
// Restore the original drawable
replaceTargetDrawables(mContext.getResources(), existingResId, existingResId);
}
return replaced;
}
}
42 changes: 16 additions & 26 deletions packages/SystemUI/src/com/android/systemui/SearchPanelView.java
Expand Up @@ -53,7 +53,6 @@ public class SearchPanelView extends FrameLayout implements
private static final String ASSIST_ICON_METADATA_NAME =
"com.android.systemui.action_assist_icon";
private final Context mContext;
private final SearchManager mSearchManager;
private BaseStatusBar mBar;
private StatusBarTouchProxy mStatusBarTouchProxy;

Expand All @@ -68,25 +67,13 @@ public SearchPanelView(Context context, AttributeSet attrs) {
public SearchPanelView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
if (mSearchManager == null) {
Slog.w(TAG, "Search manager not available");
}
}

public boolean isAssistantAvailable() {
return mSearchManager != null && mSearchManager.isAssistantAvailable();
}

private Intent getAssistIntent() {
return mSearchManager != null ? mSearchManager.getAssistIntent() : null;
}

private void startAssistActivity() {
// Close Recent Apps if needed
mBar.animateCollapse(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL);
// Launch Assist
Intent intent = getAssistIntent();
Intent intent = SearchManager.getAssistIntent(mContext);
if (intent == null) return;
try {
ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
Expand Down Expand Up @@ -150,19 +137,17 @@ protected void onFinishInflate() {
// TODO: fetch views
mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
mGlowPadView.setOnTriggerListener(mGlowPadViewListener);
if (mSearchManager != null) {
ComponentName component = mSearchManager.getGlobalSearchActivity();
if (component != null) {
if (!mGlowPadView.replaceTargetDrawablesIfPresent(component,
ASSIST_ICON_METADATA_NAME,
com.android.internal.R.drawable.ic_action_assist_generic)) {
Slog.w(TAG, "Couldn't grab icon from component " + component);
}
} else {
Slog.w(TAG, "No search icon specified in component " + component);
}

private void maybeSwapSearchIcon() {
Intent intent = SearchManager.getAssistIntent(mContext);
if (intent != null) {
ComponentName component = intent.getComponent();
if (component == null || !mGlowPadView.replaceTargetDrawablesIfPresent(component,
ASSIST_ICON_METADATA_NAME,
com.android.internal.R.drawable.ic_action_assist_generic)) {
if (DEBUG) Slog.v(TAG, "Couldn't grab icon for component " + component);
}
} else {
Slog.w(TAG, "No SearchManager");
}
}

Expand Down Expand Up @@ -210,6 +195,7 @@ public void show(final boolean show, boolean animate) {
}
mShowing = show;
if (show) {
maybeSwapSearchIcon();
if (getVisibility() != View.VISIBLE) {
setVisibility(View.VISIBLE);
// Don't start the animation until we've created the layer, which is done
Expand Down Expand Up @@ -289,4 +275,8 @@ private LayoutTransition createLayoutTransitioner() {
transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
return transitioner;
}

public boolean isAssistantAvailable() {
return SearchManager.getAssistIntent(mContext) != null;
}
}
56 changes: 16 additions & 40 deletions policy/src/com/android/internal/policy/impl/LockScreen.java
Expand Up @@ -83,7 +83,6 @@ class LockScreen extends LinearLayout implements KeyguardScreen {
private View mUnlockWidget;
private boolean mCameraDisabled;
private boolean mSearchDisabled;
private SearchManager mSearchManager;
// Is there a vibrator
private final boolean mHasVibrator;

Expand Down Expand Up @@ -253,23 +252,6 @@ public void cleanUp() {
}
}

private boolean isAssistantAvailable() {
SearchManager searchManager = getSearchManager();
return searchManager != null && searchManager.isAssistantAvailable();
}

private Intent getAssistIntent() {
SearchManager searchManager = getSearchManager();
return searchManager != null ? searchManager.getAssistIntent() : null;
}

private SearchManager getSearchManager() {
if (mSearchManager == null) {
mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
}
return mSearchManager;
}

class GlowPadViewMethods implements GlowPadView.OnTriggerListener,
UnlockWidgetCommonMethods {
private final GlowPadView mGlowPadView;
Expand Down Expand Up @@ -297,27 +279,21 @@ public void updateResources() {

// Update the search icon with drawable from the search .apk
if (!mSearchDisabled) {
SearchManager searchManager = getSearchManager();
if (searchManager != null) {
ComponentName component = searchManager.getGlobalSearchActivity();
if (component != null) {
// XXX Hack. We need to substitute the icon here but haven't formalized
// the public API. The "_google" metadata will be going away, so
// DON'T USE IT!
boolean replaced = mGlowPadView.replaceTargetDrawablesIfPresent(component,
ASSIST_ICON_METADATA_NAME + "_google",
com.android.internal.R.drawable.ic_action_assist_generic);

if (!replaced && !mGlowPadView.replaceTargetDrawablesIfPresent(component,
ASSIST_ICON_METADATA_NAME,
com.android.internal.R.drawable.ic_action_assist_generic)) {
Slog.w(TAG, "Couldn't grab icon from package " + component);
}
} else {
Slog.w(TAG, "No search icon specified in package " + component);
Intent intent = SearchManager.getAssistIntent(mContext);
if (intent != null) {
// XXX Hack. We need to substitute the icon here but haven't formalized
// the public API. The "_google" metadata will be going away, so
// DON'T USE IT!
ComponentName component = intent.getComponent();
boolean replaced = mGlowPadView.replaceTargetDrawablesIfPresent(component,
ASSIST_ICON_METADATA_NAME + "_google",
com.android.internal.R.drawable.ic_action_assist_generic);

if (!replaced && !mGlowPadView.replaceTargetDrawablesIfPresent(component,
ASSIST_ICON_METADATA_NAME,
com.android.internal.R.drawable.ic_action_assist_generic)) {
Slog.w(TAG, "Couldn't grab icon from package " + component);
}
} else {
Slog.w(TAG, "No SearchManager");
}
}

Expand All @@ -337,7 +313,7 @@ public void onTrigger(View v, int target) {
final int resId = mGlowPadView.getResourceIdForTarget(target);
switch (resId) {
case com.android.internal.R.drawable.ic_action_assist_generic:
Intent assistIntent = getAssistIntent();
Intent assistIntent = SearchManager.getAssistIntent(mContext);
if (assistIntent != null) {
launchActivity(assistIntent);
} else {
Expand Down Expand Up @@ -550,7 +526,7 @@ private void updateTargets() {
} else if (disabledBySimState) {
Log.v(TAG, "Camera disabled by Sim State");
}
boolean searchActionAvailable = isAssistantAvailable();
boolean searchActionAvailable = SearchManager.getAssistIntent(mContext) != null;
mCameraDisabled = disabledByAdmin || disabledBySimState || !cameraTargetPresent;
mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent;
mUnlockWidgetMethods.updateResources();
Expand Down
24 changes: 11 additions & 13 deletions policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
Expand Up @@ -2081,6 +2081,8 @@ private void launchAssistLongPressAction() {
Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
// TODO: This only stops the factory-installed search manager.
// Need to formalize an API to handle others
SearchManager searchManager = getSearchManager();
if (searchManager != null) {
searchManager.stopSearch();
Expand All @@ -2093,19 +2095,15 @@ private void launchAssistLongPressAction() {

private void launchAssistAction() {
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);

SearchManager searchManager = getSearchManager();
if (searchManager != null) {
Intent intent = searchManager.getAssistIntent();
if (intent != null) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
mContext.startActivity(intent);
} catch (ActivityNotFoundException e) {
Slog.w(TAG, "No activity to handle assist action.", e);
}
Intent intent = SearchManager.getAssistIntent(mContext);
if (intent != null) {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_SINGLE_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
mContext.startActivity(intent);
} catch (ActivityNotFoundException e) {
Slog.w(TAG, "No activity to handle assist action.", e);
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions tests/Assistant/Android.mk
@@ -0,0 +1,11 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES := $(call all-subdir-java-files)

LOCAL_PACKAGE_NAME := Assistant

LOCAL_MODULE_TAGS := tests
LOCAL_CERTIFICATE := platform

include $(BUILD_PACKAGE)
39 changes: 39 additions & 0 deletions tests/Assistant/AndroidManifest.xml
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.test.assistant">

<application android:label="@string/activity_title">

<activity android:name=".AssistActivity"
android:theme="@android:style/Theme.NoTitleBar">

<!-- Handle assist intent -->
<intent-filter>
<action android:name="android.intent.action.ASSIST" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>

<!-- Provide icon for search -->
<meta-data android:name="com.android.systemui.action_assist_icon"
android:resource="@drawable/ic_action_assist" />

</activity>

</application>

</manifest>
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions tests/Assistant/res/drawable/ic_action_assist.xml
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2012 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item
android:state_enabled="true"
android:state_active="false"
android:state_focused="false"
android:drawable="@drawable/ic_action_assist_normal" />

<item
android:state_enabled="true"
android:state_active="true"
android:state_focused="false"
android:drawable="@drawable/ic_action_assist_activated" />

<item
android:state_enabled="true"
android:state_active="false"
android:state_focused="true"
android:drawable="@drawable/ic_action_assist_activated" />

</selector>

0 comments on commit 45308b1

Please sign in to comment.