TextClassifier: Support service intents.

Previously, the TextClassifier only supported Activity intents.

Test: bit FrameworksCoreTests:android.view.textclassifier.TextClassificationManagerTest
Test: bit FrameworksCoreTests:android.widget.TextViewActivityTest
Change-Id: Ic488e2f6241eb91a6cd6e16d9f84a49a679164dc
This commit is contained in:
Abodunrinwa Toki
2018-02-12 19:59:28 +00:00
parent 2ab510ee24
commit 2f19b92c75
2 changed files with 75 additions and 34 deletions

View File

@@ -17,11 +17,14 @@
package android.view.textclassifier;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -36,6 +39,8 @@ import android.view.textclassifier.TextClassifier.EntityType;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
@@ -81,7 +86,7 @@ import java.util.Map;
* // Add the "secondary" actions.
* for (int i = 0; i < classification.getSecondaryActionsCount(); i++) {
* if (thisAppHasPermissionToInvokeIntent(classification.getSecondaryIntent(i))) {
* menu.add(Menu.NONE, i + 1, 20, classification.getSecondaryLabel(i))
* menu.add(Menu.NONE, i + 1, 20, classification.getSecondaryLabel(i))
* .setIcon(classification.getSecondaryIcon(i))
* .setIntent(classification.getSecondaryIntent(i));
* }
@@ -109,6 +114,14 @@ public final class TextClassification implements Parcelable {
private static final int MAX_PRIMARY_ICON_SIZE = 192;
private static final int MAX_SECONDARY_ICON_SIZE = 144;
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {IntentType.UNSUPPORTED, IntentType.ACTIVITY, IntentType.SERVICE})
private @interface IntentType {
int UNSUPPORTED = -1;
int ACTIVITY = 0;
int SERVICE = 1;
}
@NonNull private final String mText;
@Nullable private final Drawable mPrimaryIcon;
@Nullable private final String mPrimaryLabel;
@@ -312,17 +325,58 @@ public final class TextClassification implements Parcelable {
}
/**
* Creates an OnClickListener that starts an activity with the specified intent.
* Creates an OnClickListener that triggers the specified intent.
* Returns null if the intent is not supported for the specified context.
*
* @throws IllegalArgumentException if context or intent is null
* @hide
*/
@NonNull
public static OnClickListener createStartActivityOnClickListener(
@Nullable
public static OnClickListener createIntentOnClickListener(
@NonNull final Context context, @NonNull final Intent intent) {
switch (getIntentType(intent, context)) {
case IntentType.ACTIVITY:
return v -> context.startActivity(intent);
case IntentType.SERVICE:
return v -> context.startService(intent);
default:
return null;
}
}
@IntentType
private static int getIntentType(@NonNull Intent intent, @NonNull Context context) {
Preconditions.checkArgument(context != null);
Preconditions.checkArgument(intent != null);
return v -> context.startActivity(intent);
final ResolveInfo activityRI = context.getPackageManager().resolveActivity(intent, 0);
if (activityRI != null) {
if (context.getPackageName().equals(activityRI.activityInfo.packageName)) {
return IntentType.ACTIVITY;
}
final boolean exported = activityRI.activityInfo.exported;
if (exported && hasPermission(context, activityRI.activityInfo.permission)) {
return IntentType.ACTIVITY;
}
}
final ResolveInfo serviceRI = context.getPackageManager().resolveService(intent, 0);
if (serviceRI != null) {
if (context.getPackageName().equals(serviceRI.serviceInfo.packageName)) {
return IntentType.SERVICE;
}
final boolean exported = serviceRI.serviceInfo.exported;
if (exported && hasPermission(context, serviceRI.serviceInfo.permission)) {
return IntentType.SERVICE;
}
}
return IntentType.UNSUPPORTED;
}
private static boolean hasPermission(@NonNull Context context, @NonNull String permission) {
return permission == null
|| context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
}
/**

View File

@@ -4020,10 +4020,11 @@ public class Editor {
if (textClassification == null) {
return;
}
if (isValidAssistMenuItem(
final OnClickListener onClick = getSupportedOnClickListener(
textClassification.getIcon(),
textClassification.getLabel(),
textClassification.getIntent())) {
textClassification.getIntent());
if (onClick != null) {
final MenuItem item = menu.add(
TextView.ID_ASSIST, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST,
textClassification.getLabel())
@@ -4031,15 +4032,16 @@ public class Editor {
.setIntent(textClassification.getIntent());
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
mAssistClickHandlers.put(
item, TextClassification.createStartActivityOnClickListener(
item, TextClassification.createIntentOnClickListener(
mTextView.getContext(), textClassification.getIntent()));
}
final int count = textClassification.getSecondaryActionsCount();
for (int i = 0; i < count; i++) {
if (!isValidAssistMenuItem(
final OnClickListener onClick1 = getSupportedOnClickListener(
textClassification.getSecondaryIcon(i),
textClassification.getSecondaryLabel(i),
textClassification.getSecondaryIntent(i))) {
textClassification.getSecondaryIntent(i));
if (onClick1 == null) {
continue;
}
final int order = MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i;
@@ -4050,7 +4052,7 @@ public class Editor {
.setIntent(textClassification.getSecondaryIntent(i));
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
mAssistClickHandlers.put(item,
TextClassification.createStartActivityOnClickListener(
TextClassification.createIntentOnClickListener(
mTextView.getContext(), textClassification.getSecondaryIntent(i)));
}
}
@@ -4067,30 +4069,15 @@ public class Editor {
}
}
private boolean isValidAssistMenuItem(Drawable icon, CharSequence label, Intent intent) {
@Nullable
private OnClickListener getSupportedOnClickListener(
Drawable icon, CharSequence label, Intent intent) {
final boolean hasUi = icon != null || !TextUtils.isEmpty(label);
final boolean hasAction = isSupportedIntent(intent);
return hasUi && hasAction;
}
private boolean isSupportedIntent(Intent intent) {
if (intent == null) {
return false;
if (hasUi) {
return TextClassification.createIntentOnClickListener(
mTextView.getContext(), intent);
}
final Context context = mTextView.getContext();
final ResolveInfo info = context.getPackageManager().resolveActivity(intent, 0);
final boolean samePackage = context.getPackageName().equals(
info.activityInfo.packageName);
if (samePackage) {
return true;
}
final boolean exported = info.activityInfo.exported;
final boolean requiresPermission = info.activityInfo.permission != null;
final boolean hasPermission = !requiresPermission
|| context.checkSelfPermission(info.activityInfo.permission)
== PackageManager.PERMISSION_GRANTED;
return exported && hasPermission;
return null;
}
private boolean onAssistMenuItemClicked(MenuItem assistMenuItem) {
@@ -4107,7 +4094,7 @@ public class Editor {
if (onClickListener == null) {
final Intent intent = assistMenuItem.getIntent();
if (intent != null) {
onClickListener = TextClassification.createStartActivityOnClickListener(
onClickListener = TextClassification.createIntentOnClickListener(
mTextView.getContext(), intent);
}
}