Merge "Do not parcel legacy TextClassification fields" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
6db14d01c2
@@ -28,10 +28,11 @@ import android.content.Intent;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
import android.os.LocaleList;
|
import android.os.LocaleList;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
@@ -68,7 +69,7 @@ import java.util.Map;
|
|||||||
* Button button = new Button(context);
|
* Button button = new Button(context);
|
||||||
* button.setCompoundDrawablesWithIntrinsicBounds(classification.getIcon(), null, null, null);
|
* button.setCompoundDrawablesWithIntrinsicBounds(classification.getIcon(), null, null, null);
|
||||||
* button.setText(classification.getLabel());
|
* button.setText(classification.getLabel());
|
||||||
* button.setOnClickListener(v -> context.startActivity(classification.getIntent()));
|
* button.setOnClickListener(v -> classification.getActions().get(0).getActionIntent().send());
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <p>e.g. starting an action mode with menu items that can handle the classified text:
|
* <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.
|
* 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 Use {@link #getActions()} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@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.
|
* 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 Use {@link #getActions()} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@@ -216,6 +223,9 @@ public final class TextClassification implements Parcelable {
|
|||||||
/**
|
/**
|
||||||
* Returns an intent that may be fired to act on the classified text.
|
* 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 Use {@link #getActions()} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@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
|
* Returns the OnClickListener that may be triggered to act on the classified text.
|
||||||
* 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
|
* <p><strong>NOTE: </strong>This field is not parcelable and only represents the first
|
||||||
* fail if the activity doesn't have permission to send the intent.
|
* {@link RemoteAction} (if one exists) when this object is read from a parcel.
|
||||||
*
|
*
|
||||||
* @deprecated Use {@link #getActions()} instead.
|
* @deprecated Use {@link #getActions()} instead.
|
||||||
*/
|
*/
|
||||||
@@ -323,41 +333,6 @@ public final class TextClassification implements Parcelable {
|
|||||||
|| context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
|
|| 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.
|
* 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
|
* Sets the icon for the <i>primary</i> action that may be rendered on a widget used to act
|
||||||
* on the classified text.
|
* 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 Use {@link #addAction(RemoteAction)} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@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
|
* Sets the label for the <i>primary</i> action that may be rendered on a widget used to
|
||||||
* act on the classified text.
|
* 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 Use {@link #addAction(RemoteAction)} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@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
|
* Sets the intent for the <i>primary</i> action that may be fired to act on the classified
|
||||||
* text.
|
* text.
|
||||||
*
|
*
|
||||||
|
* <p><strong>NOTE: </strong>This field is not parcelled.
|
||||||
|
*
|
||||||
* @deprecated Use {@link #addAction(RemoteAction)} instead.
|
* @deprecated Use {@link #addAction(RemoteAction)} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@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
|
* 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
|
* the classified text.
|
||||||
* object is read from a parcel.
|
*
|
||||||
|
* <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.
|
* @deprecated Use {@link #addAction(RemoteAction)} instead.
|
||||||
*/
|
*/
|
||||||
@@ -674,17 +659,7 @@ public final class TextClassification implements Parcelable {
|
|||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeString(mText);
|
dest.writeString(mText);
|
||||||
final Bitmap legacyIconBitmap = drawableToBitmap(mLegacyIcon, MAX_LEGACY_ICON_SIZE);
|
// NOTE: legacy fields are not parcelled.
|
||||||
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.
|
|
||||||
dest.writeTypedList(mActions);
|
dest.writeTypedList(mActions);
|
||||||
mEntityConfidence.writeToParcel(dest, flags);
|
mEntityConfidence.writeToParcel(dest, flags);
|
||||||
dest.writeString(mId);
|
dest.writeString(mId);
|
||||||
@@ -705,23 +680,43 @@ public final class TextClassification implements Parcelable {
|
|||||||
|
|
||||||
private TextClassification(Parcel in) {
|
private TextClassification(Parcel in) {
|
||||||
mText = in.readString();
|
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;
|
|
||||||
} else {
|
|
||||||
mLegacyIntent = Intent.CREATOR.createFromParcel(in);
|
|
||||||
mLegacyIntent.removeFlags(
|
|
||||||
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
|
||||||
}
|
|
||||||
mLegacyOnClickListener = null; // not parcelable
|
|
||||||
mActions = in.createTypedArrayList(RemoteAction.CREATOR);
|
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 {
|
||||||
|
mLegacyIcon = null;
|
||||||
|
mLegacyLabel = null;
|
||||||
|
mLegacyOnClickListener = null;
|
||||||
|
}
|
||||||
|
mLegacyIntent = null; // mLegacyIntent is not parcelled.
|
||||||
mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
|
mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
|
||||||
mId = in.readString();
|
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.
|
// TODO: Remove once apps can build against the latest sdk.
|
||||||
/**
|
/**
|
||||||
* Optional input parameters for generating TextClassification.
|
* Optional input parameters for generating TextClassification.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package android.view.textclassifier;
|
package android.view.textclassifier;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
@@ -26,6 +27,7 @@ import android.content.Intent;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.Icon;
|
import android.graphics.drawable.Icon;
|
||||||
import android.os.LocaleList;
|
import android.os.LocaleList;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
@@ -99,12 +101,6 @@ public class TextClassificationTest {
|
|||||||
assertEquals(id, result.getId());
|
assertEquals(id, result.getId());
|
||||||
assertEquals(2, result.getActions().size());
|
assertEquals(2, result.getActions().size());
|
||||||
|
|
||||||
// Legacy API.
|
|
||||||
assertNull(result.getIcon());
|
|
||||||
assertNull(result.getLabel());
|
|
||||||
assertNull(result.getIntent());
|
|
||||||
assertNull(result.getOnClickListener());
|
|
||||||
|
|
||||||
// Primary action.
|
// Primary action.
|
||||||
final RemoteAction primaryAction = result.getActions().get(0);
|
final RemoteAction primaryAction = result.getActions().get(0);
|
||||||
assertEquals(primaryLabel, primaryAction.getTitle());
|
assertEquals(primaryLabel, primaryAction.getTitle());
|
||||||
@@ -128,23 +124,35 @@ public class TextClassificationTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testParcelLegacy() {
|
public void testParcelLegacy() {
|
||||||
final Context context = InstrumentationRegistry.getInstrumentation().getContext();
|
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 String label = "label";
|
||||||
final Intent intent = new Intent("intent");
|
final PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||||
final View.OnClickListener onClickListener = v -> { };
|
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()
|
final TextClassification reference = new TextClassification.Builder()
|
||||||
.setText(text)
|
.setIcon(legacyIcon)
|
||||||
.setIcon(icon.loadDrawable(context))
|
.setLabel(legacyLabel)
|
||||||
.setLabel(label)
|
.setIntent(legacyIntent)
|
||||||
.setIntent(intent)
|
.setOnClickListener(legacyOnClick)
|
||||||
.setOnClickListener(onClickListener)
|
.addAction(remoteAction)
|
||||||
.setEntityType(TextClassifier.TYPE_ADDRESS, 0.3f)
|
|
||||||
.setEntityType(TextClassifier.TYPE_PHONE, 0.7f)
|
|
||||||
.setId(id)
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Parcel and unparcel
|
// Parcel and unparcel
|
||||||
@@ -153,13 +161,14 @@ public class TextClassificationTest {
|
|||||||
parcel.setDataPosition(0);
|
parcel.setDataPosition(0);
|
||||||
final TextClassification result = TextClassification.CREATOR.createFromParcel(parcel);
|
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();
|
final Bitmap resultIcon = ((BitmapDrawable) result.getIcon()).getBitmap();
|
||||||
assertEquals(icon.getBitmap().getPixel(0, 0), resultIcon.getPixel(0, 0));
|
assertEquals(iconColor, resultIcon.getPixel(0, 0));
|
||||||
assertEquals(192, resultIcon.getWidth());
|
assertEquals(width, resultIcon.getWidth());
|
||||||
assertEquals(96, resultIcon.getHeight());
|
assertEquals(height, resultIcon.getHeight());
|
||||||
assertEquals(label, result.getLabel());
|
assertEquals(label, result.getLabel());
|
||||||
assertEquals(intent.getAction(), result.getIntent().getAction());
|
assertNotNull(result.getOnClickListener());
|
||||||
assertNull(result.getOnClickListener());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user