Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fafdb737 authored by Abodunrinwa Toki's avatar Abodunrinwa Toki
Browse files

Fix TextClassifier.getTextClassificationResult()

Gracefully handles situations where a default app cannot be found
to handle the intent.

1. If we can not find any app to handle the intent,
   do not include an "assist" menu item entry to fire the intent.
   Also, do not linkify the entry.
2. If we do not have a default app to handle the intent,
   show a generic title for apps that will handle the intent
   and do not include an icon.

In the ideal case where we find a default app to include the intent,
show the app's (preferably activity's) title and icon.

Test: Manually tested. There's an AI to write more automated tests.
Bug: 34777322
Bug: 34927631
Change-Id: Ia94efbbdda3da8f181fac9228cd2d3a76cb727d3
parent 580f32d7
Loading
Loading
Loading
Loading
+37 −15
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package android.view.textclassifier;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
@@ -157,18 +158,29 @@ final class TextClassifierImpl implements TextClassifier {
                .setEntityType(type, 1.0f /* confidence */)
                .setEntityType(type, 1.0f /* confidence */)
                .setIntent(intent)
                .setIntent(intent)
                .setOnClickListener(TextClassificationResult.createStartActivityOnClick(
                .setOnClickListener(TextClassificationResult.createStartActivityOnClick(
                        mContext, intent))
                        mContext, intent));
                .setLabel(IntentFactory.getLabel(mContext, type));
        final PackageManager pm = mContext.getPackageManager();
        final PackageManager pm = mContext.getPackageManager();
        final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
        final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
        // TODO: If the resolveInfo is the "chooser", do not set the package name and use a default
        if (resolveInfo != null && resolveInfo.activityInfo != null) {
        // icon for this classification type.
            final String packageName = resolveInfo.activityInfo.packageName;
        intent.setPackage(resolveInfo.activityInfo.packageName);
            if ("android".equals(packageName)) {
                // Requires the chooser to find an activity to handle the intent.
                builder.setLabel(IntentFactory.getLabel(mContext, type));
            } else {
                // A default activity will handle the intent.
                intent.setComponent(new ComponentName(packageName, resolveInfo.activityInfo.name));
                Drawable icon = resolveInfo.activityInfo.loadIcon(pm);
                Drawable icon = resolveInfo.activityInfo.loadIcon(pm);
                if (icon == null) {
                if (icon == null) {
                    icon = resolveInfo.loadIcon(pm);
                    icon = resolveInfo.loadIcon(pm);
                }
                }
                builder.setIcon(icon);
                builder.setIcon(icon);
                CharSequence label = resolveInfo.activityInfo.loadLabel(pm);
                if (label == null) {
                    label = resolveInfo.loadLabel(pm);
                }
                builder.setLabel(label != null ? label.toString() : null);
            }
        }
        return builder.build();
        return builder.build();
    }
    }


@@ -211,11 +223,14 @@ final class TextClassifierImpl implements TextClassifier {
                    final String type =
                    final String type =
                            smartSelection.classifyText(text, selectionStart, selectionEnd);
                            smartSelection.classifyText(text, selectionStart, selectionEnd);
                    if (matches(type, linkMask)) {
                    if (matches(type, linkMask)) {
                        final ClickableSpan span = createSpan(
                        final Intent intent = IntentFactory.create(
                                context, type, text.substring(selectionStart, selectionEnd));
                                type, text.substring(selectionStart, selectionEnd));
                        if (hasActivityHandler(context, intent)) {
                            final ClickableSpan span = createSpan(context, intent);
                            spans.add(new SpanSpec(selectionStart, selectionEnd, span));
                            spans.add(new SpanSpec(selectionStart, selectionEnd, span));
                        }
                        }
                    }
                    }
                }
                start = end;
                start = end;
            }
            }
            return new LinksInfoImpl(text, avoidOverlaps(spans, text));
            return new LinksInfoImpl(text, avoidOverlaps(spans, text));
@@ -279,17 +294,24 @@ final class TextClassifierImpl implements TextClassifier {
            return result;
            return result;
        }
        }


        private static ClickableSpan createSpan(
        private static ClickableSpan createSpan(final Context context, final Intent intent) {
                final Context context, final String type, final String text) {
            return new ClickableSpan() {
            return new ClickableSpan() {
                // TODO: Style this span.
                // TODO: Style this span.
                @Override
                @Override
                public void onClick(View widget) {
                public void onClick(View widget) {
                    context.startActivity(IntentFactory.create(type, text));
                    context.startActivity(intent);
                }
                }
            };
            };
        }
        }


        private static boolean hasActivityHandler(Context context, @Nullable Intent intent) {
            if (intent == null) {
                return false;
            }
            final ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0);
            return resolveInfo != null && resolveInfo.activityInfo != null;
        }

        /**
        /**
         * Implementation of LinksInfo that adds ClickableSpans to the specified text.
         * Implementation of LinksInfo that adds ClickableSpans to the specified text.
         */
         */
+27 −8
Original line number Original line Diff line number Diff line
@@ -3917,14 +3917,20 @@ public class Editor {
        private void updateAssistMenuItem(
        private void updateAssistMenuItem(
                Menu menu, TextClassificationResult textClassificationResult) {
                Menu menu, TextClassificationResult textClassificationResult) {
            menu.removeItem(TextView.ID_ASSIST);
            menu.removeItem(TextView.ID_ASSIST);
            if (textClassificationResult != null
            if (textClassificationResult != null) {
                    && textClassificationResult.getIcon() != null
                final Drawable icon = textClassificationResult.getIcon();
                    && textClassificationResult.getOnClickListener() != null) {
                final CharSequence label = textClassificationResult.getLabel();
                menu.add(Menu.NONE, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST, null)
                final OnClickListener onClickListener =
                        .setIcon(textClassificationResult.getIcon())
                        textClassificationResult.getOnClickListener();
                final Intent intent = textClassificationResult.getIntent();
                if ((icon != null || !TextUtils.isEmpty(label))
                        && (onClickListener != null || intent != null)) {
                    menu.add(Menu.NONE, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST, label)
                            .setIcon(icon)
                            .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
                            .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
                }
                }
            }
            }
        }


        @Override
        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
@@ -3935,9 +3941,22 @@ public class Editor {
            if (customCallback != null && customCallback.onActionItemClicked(mode, item)) {
            if (customCallback != null && customCallback.onActionItemClicked(mode, item)) {
                return true;
                return true;
            }
            }
            if (TextView.ID_ASSIST == item.getItemId() && mTextClassificationResult != null) {
            final TextClassificationResult textClassificationResult = mTextClassificationResult;
                mTextClassificationResult.getOnClickListener().onClick(mTextView);
            if (TextView.ID_ASSIST == item.getItemId() && textClassificationResult != null) {
                final OnClickListener onClickListener =
                        textClassificationResult.getOnClickListener();
                if (onClickListener != null) {
                    onClickListener.onClick(mTextView);
                } else {
                    final Intent intent = textClassificationResult.getIntent();
                    if (intent != null) {
                        TextClassificationResult.createStartActivityOnClick(
                                mTextView.getContext(), intent)
                                .onClick(mTextView);
                    }
                }
                stopTextActionMode();
                stopTextActionMode();
                return true;
            }
            }
            return mTextView.onTextContextMenuItem(item.getItemId());
            return mTextView.onTextContextMenuItem(item.getItemId());
        }
        }