Merge "Support seralization of bitmap and adaptive icons" into androidx-main
diff --git a/wear/watchface/watchface-complications-data/proguard-rules.pro b/wear/watchface/watchface-complications-data/proguard-rules.pro
index de1e0af..187c816 100644
--- a/wear/watchface/watchface-complications-data/proguard-rules.pro
+++ b/wear/watchface/watchface-complications-data/proguard-rules.pro
@@ -22,6 +22,7 @@
 -keep public class android.support.wearable.complications.TimeDependentText { *; }
 -keep public class android.support.wearable.complications.TimeDifferenceText { *; }
 -keep public class android.support.wearable.complications.TimeFormatText { *; }
+-keep public class android.graphics.drawable.Icon { *; }
 
 # Ensure our sanitizing of EditorSession.usr_style doesn't break due to renames.
 -keep public class kotlinx.coroutines.flow.MutableStateFlow { *; }
diff --git a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java
index ec2444f..e584635 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java
+++ b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java
@@ -536,7 +536,7 @@
 
     @RequiresApi(api = Build.VERSION_CODES.P)
     private static class SerializedForm implements Serializable {
-        private static final int VERSION_NUMBER = 15;
+        private static final int VERSION_NUMBER = 16;
 
         @NonNull
         ComplicationData mComplicationData;
diff --git a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/IconSerializableHelper.java b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/IconSerializableHelper.java
index 9b0bfa4..4dea811 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/IconSerializableHelper.java
+++ b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/IconSerializableHelper.java
@@ -16,16 +16,22 @@
 
 package android.support.wearable.complications;
 
+import android.annotation.SuppressLint;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.drawable.Icon;
 import android.os.Build;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.Serializable;
+import java.lang.reflect.Method;
 
 @RequiresApi(api = Build.VERSION_CODES.P)
 class IconSerializableHelper implements Serializable {
@@ -33,6 +39,9 @@
     String mResourcePackage;
     int mResourceId;
     String mUri;
+    byte[] mBitmap;
+
+    private static final String TAG = "IconSerializableHelper";
 
     @Nullable
     static IconSerializableHelper create(@Nullable Icon icon) {
@@ -61,22 +70,44 @@
                 break;
 
             case Icon.TYPE_URI:
+            case Icon.TYPE_URI_ADAPTIVE_BITMAP:
                 mUri = icon.getUri().toString();
                 break;
-        }
 
-        // We currently don't attempt to serialize any other type of icon. We could render to a
-        // bitmap, but the above covers the majority of complication icons.
+            case Icon.TYPE_BITMAP:
+            case Icon.TYPE_ADAPTIVE_BITMAP:
+                try {
+                    Method getBitmap = icon.getClass().getDeclaredMethod("getBitmap");
+                    @SuppressLint("BanUncheckedReflection")
+                    Bitmap bitmap = (Bitmap) getBitmap.invoke(icon);
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
+                    mBitmap = baos.toByteArray();
+                } catch (Exception e) {
+                    Log.e(TAG, "Failed to serialize bitmap", e);
+                }
+                break;
+
+            default:
+                Log.e(TAG, "Failed to serialize icon of type " + mType);
+        }
     }
 
-    @Nullable Icon toIcon() {
+    @Nullable
+    Icon toIcon() {
         switch (mType) {
             case Icon.TYPE_RESOURCE:
                 return Icon.createWithResource(mResourcePackage, mResourceId);
 
             case Icon.TYPE_URI:
+            case Icon.TYPE_URI_ADAPTIVE_BITMAP:
                 return Icon.createWithContentUri(mUri);
 
+            case Icon.TYPE_BITMAP:
+            case Icon.TYPE_ADAPTIVE_BITMAP:
+                return Icon.createWithBitmap(BitmapFactory.decodeByteArray(mBitmap, 0,
+                        mBitmap.length));
+
             default:
                 return null;
         }
diff --git a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
index 37edec2..73c4f3b 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
@@ -16,10 +16,12 @@
 
 package androidx.wear.watchface.complications.data
 
+import android.annotation.SuppressLint
 import android.app.PendingIntent
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.graphics.Bitmap
 import android.graphics.Color
 import android.graphics.drawable.Icon
 import android.os.Build
@@ -1146,6 +1148,36 @@
 
     @RequiresApi(Build.VERSION_CODES.P)
     @Test
+    public fun smallImageComplicationData_with_BitmapIcon() {
+        val bitmapIcon =
+            Icon.createWithBitmap(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888))
+        val image = SmallImage.Builder(bitmapIcon, SmallImageType.PHOTO).build()
+        val data = SmallImageComplicationData.Builder(
+            image, "content description".complicationText
+        ).setDataSource(dataSourceA).build()
+        ParcelableSubject.assertThat(data.asWireComplicationData())
+            .hasSameSerializationAs(
+                WireComplicationDataBuilder(WireComplicationData.TYPE_SMALL_IMAGE)
+                    .setSmallImage(bitmapIcon)
+                    .setSmallImageStyle(WireComplicationData.IMAGE_STYLE_PHOTO)
+                    .setContentDescription(WireComplicationText.plainText("content description"))
+                    .setDataSource(dataSourceA)
+                    .build()
+            )
+        testRoundTripConversions(data)
+        val deserialized = serializeAndDeserialize(data) as SmallImageComplicationData
+
+        assertThat(deserialized.smallImage.image.type).isEqualTo(Icon.TYPE_BITMAP)
+        val getBitmap = deserialized.smallImage.image.javaClass.getDeclaredMethod("getBitmap")
+        @SuppressLint("BanUncheckedReflection")
+        val bitmap = getBitmap.invoke(deserialized.smallImage.image) as Bitmap
+
+        assertThat(bitmap.width).isEqualTo(100)
+        assertThat(bitmap.height).isEqualTo(100)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.P)
+    @Test
     public fun backgroundImageComplicationData() {
         val photoImage = Icon.createWithContentUri("someuri")
         val data = PhotoImageComplicationData.Builder(