diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index 8fe1d8fcc805f..c91116a87998a 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -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; } /** diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 998866137dcf4..892b8b6352079 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -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); } }