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

Commit 7c41320e authored by Abodunrinwa Toki's avatar Abodunrinwa Toki Committed by android-build-merger
Browse files

Merge "Do not parcel legacy TextClassification fields" into pi-dev am: 6db14d01

am: 16418312

Change-Id: I2a7b4323e815f5a4868085be69e417439729ff90
parents 077714ce 16418312
Loading
Loading
Loading
Loading
+61 −66
Original line number Diff line number Diff line
@@ -28,10 +28,11 @@ 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;
import android.graphics.BitmapFactory;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
@@ -68,7 +69,7 @@ import java.util.Map;
 *   Button button = new Button(context);
 *   button.setCompoundDrawablesWithIntrinsicBounds(classification.getIcon(), null, null, null);
 *   button.setText(classification.getLabel());
 *   button.setOnClickListener(v -> context.startActivity(classification.getIntent()));
 *   button.setOnClickListener(v -> classification.getActions().get(0).getActionIntent().send());
 * }</pre>
 *
 * <p>e.g. starting an action mode with menu items that can handle the classified text:
@@ -194,6 +195,9 @@ public final class TextClassification implements Parcelable {
    /**
     * Returns an icon that may be rendered on a widget used to act on the classified text.
     *
     * <p><strong>NOTE: </strong>This field is not parcelable and only represents the icon of the
     * first {@link RemoteAction} (if one exists) when this object is read from a parcel.
     *
     * @deprecated Use {@link #getActions()} instead.
     */
    @Deprecated
@@ -205,6 +209,9 @@ public final class TextClassification implements Parcelable {
    /**
     * Returns a label that may be rendered on a widget used to act on the classified text.
     *
     * <p><strong>NOTE: </strong>This field is not parcelable and only represents the label of the
     * first {@link RemoteAction} (if one exists) when this object is read from a parcel.
     *
     * @deprecated Use {@link #getActions()} instead.
     */
    @Deprecated
@@ -216,6 +223,9 @@ public final class TextClassification implements Parcelable {
    /**
     * Returns an intent that may be fired to act on the classified text.
     *
     * <p><strong>NOTE: </strong>This field is not parcelled and will always return null when this
     * object is read from a parcel.
     *
     * @deprecated Use {@link #getActions()} instead.
     */
    @Deprecated
@@ -225,10 +235,10 @@ public final class TextClassification implements Parcelable {
    }

    /**
     * Returns the OnClickListener that may be triggered to act on the classified text. This field
     * is not parcelable and will be null for all objects read from a parcel. Instead, call
     * Context#startActivity(Intent) with the result of #getSecondaryIntent(int). Note that this may
     * fail if the activity doesn't have permission to send the intent.
     * Returns the OnClickListener that may be triggered to act on the classified text.
     *
     * <p><strong>NOTE: </strong>This field is not parcelable and only represents the first
     * {@link RemoteAction} (if one exists) when this object is read from a parcel.
     *
     * @deprecated Use {@link #getActions()} instead.
     */
@@ -323,41 +333,6 @@ public final class TextClassification implements Parcelable {
                || context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
    }

    /**
     * Returns a Bitmap representation of the Drawable
     *
     * @param drawable The drawable to convert.
     * @param maxDims The maximum edge length of the resulting bitmap (in pixels).
     */
    @Nullable
    private static Bitmap drawableToBitmap(@Nullable Drawable drawable, int maxDims) {
        if (drawable == null) {
            return null;
        }
        final int actualWidth = Math.max(1, drawable.getIntrinsicWidth());
        final int actualHeight = Math.max(1, drawable.getIntrinsicHeight());
        final double scaleWidth = ((double) maxDims) / actualWidth;
        final double scaleHeight = ((double) maxDims) / actualHeight;
        final double scale = Math.min(1.0, Math.min(scaleWidth, scaleHeight));
        final int width = (int) (actualWidth * scale);
        final int height = (int) (actualHeight * scale);
        if (drawable instanceof BitmapDrawable) {
            final BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            if (actualWidth != width || actualHeight != height) {
                return Bitmap.createScaledBitmap(
                        bitmapDrawable.getBitmap(), width, height, /*filter=*/false);
            } else {
                return bitmapDrawable.getBitmap();
            }
        } else {
            final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            final Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);
            return bitmap;
        }
    }

    /**
     * Builder for building {@link TextClassification} objects.
     *
@@ -426,6 +401,9 @@ public final class TextClassification implements Parcelable {
         * Sets the icon for the <i>primary</i> action that may be rendered on a widget used to act
         * on the classified text.
         *
         * <p><strong>NOTE: </strong>This field is not parcelled. If read from a parcel, the
         * returned icon represents the icon of the first {@link RemoteAction} (if one exists).
         *
         * @deprecated Use {@link #addAction(RemoteAction)} instead.
         */
        @Deprecated
@@ -439,6 +417,9 @@ public final class TextClassification implements Parcelable {
         * Sets the label for the <i>primary</i> action that may be rendered on a widget used to
         * act on the classified text.
         *
         * <p><strong>NOTE: </strong>This field is not parcelled. If read from a parcel, the
         * returned label represents the label of the first {@link RemoteAction} (if one exists).
         *
         * @deprecated Use {@link #addAction(RemoteAction)} instead.
         */
        @Deprecated
@@ -452,6 +433,8 @@ public final class TextClassification implements Parcelable {
         * Sets the intent for the <i>primary</i> action that may be fired to act on the classified
         * text.
         *
         * <p><strong>NOTE: </strong>This field is not parcelled.
         *
         * @deprecated Use {@link #addAction(RemoteAction)} instead.
         */
        @Deprecated
@@ -463,8 +446,10 @@ public final class TextClassification implements Parcelable {

        /**
         * Sets the OnClickListener for the <i>primary</i> action that may be triggered to act on
         * the classified text. This field is not parcelable and will always be null when the
         * object is read from a parcel.
         * the classified text.
         *
         * <p><strong>NOTE: </strong>This field is not parcelable. If read from a parcel, the
         * returned OnClickListener represents the first {@link RemoteAction} (if one exists).
         *
         * @deprecated Use {@link #addAction(RemoteAction)} instead.
         */
@@ -674,17 +659,7 @@ public final class TextClassification implements Parcelable {
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mText);
        final Bitmap legacyIconBitmap = drawableToBitmap(mLegacyIcon, MAX_LEGACY_ICON_SIZE);
        dest.writeInt(legacyIconBitmap != null ? 1 : 0);
        if (legacyIconBitmap != null) {
            legacyIconBitmap.writeToParcel(dest, flags);
        }
        dest.writeString(mLegacyLabel);
        dest.writeInt(mLegacyIntent != null ? 1 : 0);
        if (mLegacyIntent != null) {
            mLegacyIntent.writeToParcel(dest, flags);
        }
        // mOnClickListener is not parcelable.
        // NOTE: legacy fields are not parcelled.
        dest.writeTypedList(mActions);
        mEntityConfidence.writeToParcel(dest, flags);
        dest.writeString(mId);
@@ -705,23 +680,43 @@ public final class TextClassification implements Parcelable {

    private TextClassification(Parcel in) {
        mText = in.readString();
        mLegacyIcon = in.readInt() == 0
                ? null
                : new BitmapDrawable(Resources.getSystem(), Bitmap.CREATOR.createFromParcel(in));
        mLegacyLabel = in.readString();
        if (in.readInt() == 0) {
            mLegacyIntent = null;
        mActions = in.createTypedArrayList(RemoteAction.CREATOR);
        if (!mActions.isEmpty()) {
            final RemoteAction action = mActions.get(0);
            mLegacyIcon = maybeLoadDrawable(action.getIcon());
            mLegacyLabel = action.getTitle().toString();
            mLegacyOnClickListener = createIntentOnClickListener(mActions.get(0).getActionIntent());
        } else {
            mLegacyIntent = Intent.CREATOR.createFromParcel(in);
            mLegacyIntent.removeFlags(
                    Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            mLegacyIcon = null;
            mLegacyLabel = null;
            mLegacyOnClickListener = null;
        }
        mLegacyOnClickListener = null;  // not parcelable
        mActions = in.createTypedArrayList(RemoteAction.CREATOR);
        mLegacyIntent = null; // mLegacyIntent is not parcelled.
        mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
        mId = in.readString();
    }

    // Best effort attempt to try to load a drawable from the provided icon.
    @Nullable
    private static Drawable maybeLoadDrawable(Icon icon) {
        if (icon == null) {
            return null;
        }
        switch (icon.getType()) {
            case Icon.TYPE_BITMAP:
                return new BitmapDrawable(Resources.getSystem(), icon.getBitmap());
            case Icon.TYPE_ADAPTIVE_BITMAP:
                return new AdaptiveIconDrawable(null,
                        new BitmapDrawable(Resources.getSystem(), icon.getBitmap()));
            case Icon.TYPE_DATA:
                return new BitmapDrawable(
                        Resources.getSystem(),
                        BitmapFactory.decodeByteArray(
                                icon.getDataBytes(), icon.getDataOffset(), icon.getDataLength()));
        }
        return null;
    }

    // TODO: Remove once apps can build against the latest sdk.
    /**
     * Optional input parameters for generating TextClassification.
+33 −24
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.view.textclassifier;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

import android.app.PendingIntent;
@@ -26,6 +27,7 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.LocaleList;
import android.os.Parcel;
@@ -99,12 +101,6 @@ public class TextClassificationTest {
        assertEquals(id, result.getId());
        assertEquals(2, result.getActions().size());

        // Legacy API.
        assertNull(result.getIcon());
        assertNull(result.getLabel());
        assertNull(result.getIntent());
        assertNull(result.getOnClickListener());

        // Primary action.
        final RemoteAction primaryAction = result.getActions().get(0);
        assertEquals(primaryLabel, primaryAction.getTitle());
@@ -128,23 +124,35 @@ public class TextClassificationTest {
    @Test
    public void testParcelLegacy() {
        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
        final String text = "text";

        final Icon icon = generateTestIcon(384, 192, Color.BLUE);
        final int legacyIconWidth = 192;
        final int legacyIconHeight = 96;
        final int legacyIconColor = Color.BLUE;
        final Drawable legacyIcon = generateTestIcon(
                legacyIconWidth, legacyIconHeight, legacyIconColor)
                .loadDrawable(context);
        final String legacyLabel = "legacyLabel";
        final Intent legacyIntent = new Intent("ACTION_LEGACY");
        final View.OnClickListener legacyOnClick = null;

        final int width = 384;
        final int height = 192;
        final int iconColor = Color.RED;
        final String label = "label";
        final Intent intent = new Intent("intent");
        final View.OnClickListener onClickListener = v -> { };
        final PendingIntent pendingIntent = PendingIntent.getActivity(
                context, 0, new Intent("ACTION_0"), 0);
        final RemoteAction remoteAction = new RemoteAction(
                generateTestIcon(width, height, iconColor),
                label,
                "description",
                pendingIntent);

        final String id = "id";
        final TextClassification reference = new TextClassification.Builder()
                .setText(text)
                .setIcon(icon.loadDrawable(context))
                .setLabel(label)
                .setIntent(intent)
                .setOnClickListener(onClickListener)
                .setEntityType(TextClassifier.TYPE_ADDRESS, 0.3f)
                .setEntityType(TextClassifier.TYPE_PHONE, 0.7f)
                .setId(id)
                .setIcon(legacyIcon)
                .setLabel(legacyLabel)
                .setIntent(legacyIntent)
                .setOnClickListener(legacyOnClick)
                .addAction(remoteAction)
                .build();

        // Parcel and unparcel
@@ -153,13 +161,14 @@ public class TextClassificationTest {
        parcel.setDataPosition(0);
        final TextClassification result = TextClassification.CREATOR.createFromParcel(parcel);

        // Legacy fields excluding legacyIntent are replaced by first remoteAction.
        assertNull(result.getIntent());
        final Bitmap resultIcon = ((BitmapDrawable) result.getIcon()).getBitmap();
        assertEquals(icon.getBitmap().getPixel(0, 0), resultIcon.getPixel(0, 0));
        assertEquals(192, resultIcon.getWidth());
        assertEquals(96, resultIcon.getHeight());
        assertEquals(iconColor, resultIcon.getPixel(0, 0));
        assertEquals(width, resultIcon.getWidth());
        assertEquals(height, resultIcon.getHeight());
        assertEquals(label, result.getLabel());
        assertEquals(intent.getAction(), result.getIntent().getAction());
        assertNull(result.getOnClickListener());
        assertNotNull(result.getOnClickListener());
    }

    @Test