Improve the features of IllustrationPreference.

- Support the dynamic color.
  The colors of the illustration's labels can be changed.
  We create a mapping table of colors and labels.
  According to this mapping table, preference can
  apply the corresponding colors to the illustration.
- Support the middle ground view.
  User can overlay a view on top of the illustration.
- Support the auto scale of illustration.
- Update the layout to prevent the animation cropping.

Fix: 189199177
Test: rebotest and see the UI.
Change-Id: Ib02cbc5bb99ab8014d7ee7ed630a6721968a49ab
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
index f8dd384..5904589 100644
--- a/packages/SettingsLib/IllustrationPreference/Android.bp
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -19,5 +19,5 @@
     ],
 
     sdk_version: "system_current",
-    min_sdk_version: "21",
+    min_sdk_version: "28",
 }
diff --git a/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml b/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml
index dd2fa5e..0734aca 100644
--- a/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml
@@ -16,11 +16,7 @@
 -->
 
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:top="@dimen/settingslib_illustration_padding"
-        android:left="@dimen/settingslib_illustration_padding"
-        android:right="@dimen/settingslib_illustration_padding"
-        android:bottom="@dimen/settingslib_illustration_padding">
+    <item>
         <shape android:shape="rectangle">
             <solid android:color="@color/settingslib_protection_color"/>
             <corners android:radius="28dp"/>
diff --git a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
index 7d65aae..54731f5 100644
--- a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
@@ -23,14 +23,33 @@
     android:gravity="center"
     android:orientation="horizontal">
 
-    <com.airbnb.lottie.LottieAnimationView
-        android:id="@+id/lottie_view"
-        android:layout_width="412dp"
+    <LinearLayout
+        android:layout_width="match_parent"
         android:layout_height="300dp"
         android:layout_gravity="center"
-        android:clipToOutline="true"
-        android:background="@drawable/protection_background"
-        android:importantForAccessibility="no"/>
+        android:gravity="center_vertical"
+        android:padding="@dimen/settingslib_illustration_padding"
+        android:orientation="vertical">
+        <com.airbnb.lottie.LottieAnimationView
+            android:id="@+id/lottie_view"
+            android:adjustViewBounds="true"
+            android:maxWidth="412dp"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:clipToOutline="true"
+            android:background="@drawable/protection_background"
+            android:importantForAccessibility="no"/>
+    </LinearLayout>
+
+    <FrameLayout
+        android:id="@+id/middleground_layout"
+        android:layout_width="412dp"
+        android:layout_height="300dp"
+        android:padding="@dimen/settingslib_illustration_padding"
+        android:background="@android:color/transparent"
+        android:layout_gravity="center"
+        android:visibility="gone"/>
 
     <ImageView
         android:id="@+id/video_play_button"
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
index e53a43e..ead5174 100644
--- a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
@@ -17,4 +17,46 @@
 
 <resources>
     <color name="settingslib_protection_color">@android:color/white</color>
+
+    <!-- Dynamic colors-->
+    <color name="settingslib_color_blue600">#1a73e8</color>
+    <color name="settingslib_color_blue400">#669df6</color>
+    <color name="settingslib_color_blue300">#8ab4f8</color>
+    <color name="settingslib_color_blue100">#d2e3fc</color>
+    <color name="settingslib_color_blue50">#e8f0fe</color>
+    <color name="settingslib_color_green600">#1e8e3e</color>
+    <color name="settingslib_color_green400">#5bb974</color>
+    <color name="settingslib_color_green100">#ceead6</color>
+    <color name="settingslib_color_green50">#e6f4ea</color>
+    <color name="settingslib_color_red600">#d93025</color>
+    <color name="settingslib_color_red400">#ee675c</color>
+    <color name="settingslib_color_red100">#fad2cf</color>
+    <color name="settingslib_color_red50">#fce8e6</color>
+    <color name="settingslib_color_yellow600">#f9ab00</color>
+    <color name="settingslib_color_yellow400">#fcc934</color>
+    <color name="settingslib_color_yellow100">#feefc3</color>
+    <color name="settingslib_color_yellow50">#fef7e0</color>
+    <color name="settingslib_color_grey900">#202124</color>
+    <color name="settingslib_color_grey800">#3c4043</color>
+    <color name="settingslib_color_grey700">#5f6368</color>
+    <color name="settingslib_color_grey600">#80868b</color>
+    <color name="settingslib_color_grey400">#bdc1c6</color>
+    <color name="settingslib_color_grey300">#dadce0</color>
+    <color name="settingslib_color_grey200">#e8eaed</color>
+    <color name="settingslib_color_orange600">#e8710a</color>
+    <color name="settingslib_color_orange400">#fa903e</color>
+    <color name="settingslib_color_orange300">#fcad70</color>
+    <color name="settingslib_color_orange100">#fedfc8</color>
+    <color name="settingslib_color_pink600">#e52592</color>
+    <color name="settingslib_color_pink400">#ff63b8</color>
+    <color name="settingslib_color_pink300">#ff8bcb</color>
+    <color name="settingslib_color_pink100">#fdcfe8</color>
+    <color name="settingslib_color_purple600">#9334e6</color>
+    <color name="settingslib_color_purple400">#af5cf7</color>
+    <color name="settingslib_color_purple300">#c58af9</color>
+    <color name="settingslib_color_purple100">#e9d2fd</color>
+    <color name="settingslib_color_cyan600">#12b5c8</color>
+    <color name="settingslib_color_cyan400">#4ecde6</color>
+    <color name="settingslib_color_cyan300">#78d9ec</color>
+    <color name="settingslib_color_cyan100">#cbf0f8</color>
 </resources>
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml b/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml
index 79562b9..5e540d3 100644
--- a/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml
+++ b/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml
@@ -17,5 +17,5 @@
 
 <resources>
     <!-- Padding of illustration -->
-    <dimen name="settingslib_illustration_padding">16dp</dimen>
+    <dimen name="settingslib_illustration_padding">12dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java
new file mode 100644
index 0000000..cd2ddeb
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/ColorUtils.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.widget;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.ColorFilter;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.util.Pair;
+
+import com.airbnb.lottie.LottieAnimationView;
+import com.airbnb.lottie.LottieProperty;
+import com.airbnb.lottie.model.KeyPath;
+import com.airbnb.lottie.value.LottieFrameInfo;
+import com.airbnb.lottie.value.SimpleLottieValueCallback;
+
+import java.util.HashMap;
+
+/**
+ * ColorUtils is a util class which help the lottie illustration
+ * changes the color of tags in the json file.
+ */
+
+public class ColorUtils {
+
+    private static HashMap<String, Integer> sSysColors;
+    static {
+        sSysColors = new HashMap<>();
+        sSysColors.put(".colorAccent", android.R.attr.colorAccent);
+        sSysColors.put(".colorPrimary", android.R.attr.colorPrimary);
+        sSysColors.put(".colorPrimaryDark", android.R.attr.colorPrimaryDark);
+        sSysColors.put(".colorSecondary", android.R.attr.colorSecondary);
+    }
+
+    private static HashMap<String, Pair<Integer, Integer>> sFixedColors;
+    static {
+        sFixedColors = new HashMap<>();
+        sFixedColors.put(".blue600", new Pair<Integer, Integer>(
+                R.color.settingslib_color_blue600, R.color.settingslib_color_blue400));
+        sFixedColors.put(".green600", new Pair<Integer, Integer>(
+                R.color.settingslib_color_green600, R.color.settingslib_color_green400));
+        sFixedColors.put(".red600", new Pair<Integer, Integer>(
+                R.color.settingslib_color_red600, R.color.settingslib_color_red400));
+        sFixedColors.put(".yellow600", new Pair<Integer, Integer>(
+                R.color.settingslib_color_yellow600, R.color.settingslib_color_yellow400));
+        sFixedColors.put(".blue400", new Pair<Integer, Integer>(
+                R.color.settingslib_color_blue400, R.color.settingslib_color_blue100));
+        sFixedColors.put(".green400", new Pair<Integer, Integer>(
+                R.color.settingslib_color_green400, R.color.settingslib_color_green100));
+        sFixedColors.put(".red400", new Pair<Integer, Integer>(
+                R.color.settingslib_color_red400, R.color.settingslib_color_red100));
+        sFixedColors.put(".yellow400", new Pair<Integer, Integer>(
+                R.color.settingslib_color_yellow400, R.color.settingslib_color_yellow100));
+        sFixedColors.put(".blue300", new Pair<Integer, Integer>(
+                R.color.settingslib_color_blue300, R.color.settingslib_color_blue50));
+        sFixedColors.put(".blue50", new Pair<Integer, Integer>(
+                R.color.settingslib_color_blue50, R.color.settingslib_color_grey900));
+        sFixedColors.put(".green50", new Pair<Integer, Integer>(
+                R.color.settingslib_color_green50, R.color.settingslib_color_grey900));
+        sFixedColors.put(".red50", new Pair<Integer, Integer>(
+                R.color.settingslib_color_red50, R.color.settingslib_color_grey900));
+        sFixedColors.put(".yellow50", new Pair<Integer, Integer>(
+                R.color.settingslib_color_yellow50, R.color.settingslib_color_grey900));
+        // Secondary colors
+        sFixedColors.put(".orange600", new Pair<Integer, Integer>(
+                R.color.settingslib_color_orange600, R.color.settingslib_color_orange300));
+        sFixedColors.put(".pink600", new Pair<Integer, Integer>(
+                R.color.settingslib_color_pink600, R.color.settingslib_color_pink300));
+        sFixedColors.put(".purple600", new Pair<Integer, Integer>(
+                R.color.settingslib_color_purple600, R.color.settingslib_color_purple300));
+        sFixedColors.put(".cyan600", new Pair<Integer, Integer>(
+                R.color.settingslib_color_cyan600, R.color.settingslib_color_cyan300));
+        sFixedColors.put(".orange400", new Pair<Integer, Integer>(
+                R.color.settingslib_color_orange400, R.color.settingslib_color_orange100));
+        sFixedColors.put(".pink400", new Pair<Integer, Integer>(
+                R.color.settingslib_color_pink400, R.color.settingslib_color_pink100));
+        sFixedColors.put(".purple400", new Pair<Integer, Integer>(
+                R.color.settingslib_color_purple400, R.color.settingslib_color_purple100));
+        sFixedColors.put(".cyan400", new Pair<Integer, Integer>(
+                R.color.settingslib_color_cyan400, R.color.settingslib_color_cyan100));
+        sFixedColors.put(".gery400", new Pair<Integer, Integer>(
+                R.color.settingslib_color_grey400, R.color.settingslib_color_grey700));
+        sFixedColors.put(".gery300", new Pair<Integer, Integer>(
+                R.color.settingslib_color_grey300, R.color.settingslib_color_grey600));
+        sFixedColors.put(".gery200", new Pair<Integer, Integer>(
+                R.color.settingslib_color_grey200, R.color.settingslib_color_grey800));
+    }
+
+    private static boolean isDarkMode(Context context) {
+        return (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
+                == Configuration.UI_MODE_NIGHT_YES;
+    }
+
+    /**
+     * Apply the color of tags to the animation.
+     */
+    public static void applyDynamicColors(Context context, LottieAnimationView animationView) {
+        for (String key : sSysColors.keySet()) {
+            final int color = sSysColors.get(key);
+            animationView.addValueCallback(
+                    new KeyPath("**", key, "**"),
+                    LottieProperty.COLOR_FILTER,
+                    new SimpleLottieValueCallback<ColorFilter>() {
+                        @Override
+                        public ColorFilter getValue(LottieFrameInfo<ColorFilter> frameInfo) {
+                            return new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN);
+                        }
+                    }
+            );
+        }
+        for (String key : sFixedColors.keySet()) {
+            final Pair<Integer, Integer> fixedColorPair = sFixedColors.get(key);
+            final int color = isDarkMode(context) ? fixedColorPair.second : fixedColorPair.first;
+            animationView.addValueCallback(
+                    new KeyPath("**", key, "**"),
+                    LottieProperty.COLOR_FILTER,
+                    new SimpleLottieValueCallback<ColorFilter>() {
+                        @Override
+                        public ColorFilter getValue(LottieFrameInfo<ColorFilter> frameInfo) {
+                            return new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN);
+                        }
+                    }
+            );
+        }
+    }
+}
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index 90b8a32..7b2a0af 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -23,6 +23,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 
 import androidx.annotation.VisibleForTesting;
@@ -38,10 +39,14 @@
 public class IllustrationPreference extends Preference implements OnPreferenceClickListener {
 
     static final String TAG = "IllustrationPreference";
+
     private int mAnimationId;
     private boolean mIsAnimating;
+    private boolean mIsAutoScale;
     private ImageView mPlayButton;
     private LottieAnimationView mIllustrationView;
+    private View mMiddleGroundView;
+    private FrameLayout mMiddleGroundLayout;
 
     public IllustrationPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -66,13 +71,20 @@
             Log.w(TAG, "Invalid illustration resource id.");
             return;
         }
+        mMiddleGroundLayout = (FrameLayout) holder.findViewById(R.id.middleground_layout);
         mPlayButton = (ImageView) holder.findViewById(R.id.video_play_button);
         mIllustrationView = (LottieAnimationView) holder.findViewById(R.id.lottie_view);
         mIllustrationView.setAnimation(mAnimationId);
         mIllustrationView.loop(true);
+        ColorUtils.applyDynamicColors(getContext(), mIllustrationView);
         mIllustrationView.playAnimation();
         updateAnimationStatus(mIsAnimating);
-        setOnPreferenceClickListener(this);
+        if (mIsAutoScale) {
+            enableAnimationAutoScale(mIsAutoScale);
+        }
+        if (mMiddleGroundView != null) {
+            enableMiddleGroundView();
+        }
     }
 
     @Override
@@ -102,16 +114,59 @@
         return mIllustrationView.isAnimating();
     }
 
+    /**
+     * Set the middle ground view to preference. The user
+     * can overlay a view on top of the animation.
+     */
+    public void setMiddleGroundView(View view) {
+        mMiddleGroundView = view;
+        if (mMiddleGroundLayout == null) {
+            return;
+        }
+        enableMiddleGroundView();
+    }
+
+    /**
+     * Remove the middle ground view of preference.
+     */
+    public void removeMiddleGroundView() {
+        if (mMiddleGroundLayout == null) {
+            return;
+        }
+        mMiddleGroundLayout.removeAllViews();
+        mMiddleGroundLayout.setVisibility(View.GONE);
+    }
+
+    /**
+     * Enables the auto scale feature of animation view.
+     */
+    public void enableAnimationAutoScale(boolean enable) {
+        mIsAutoScale = enable;
+        if (mIllustrationView == null) {
+            return;
+        }
+        mIllustrationView.setScaleType(
+                mIsAutoScale ? ImageView.ScaleType.CENTER_CROP : ImageView.ScaleType.CENTER_INSIDE);
+    }
+
+    private void enableMiddleGroundView() {
+        mMiddleGroundLayout.removeAllViews();
+        mMiddleGroundLayout.addView(mMiddleGroundView);
+        mMiddleGroundLayout.setVisibility(View.VISIBLE);
+    }
+
     private void init(Context context, AttributeSet attrs) {
         setLayoutResource(R.layout.illustration_preference);
 
         mIsAnimating = true;
+        mIsAutoScale = false;
         if (attrs != null) {
             final TypedArray a = context.obtainStyledAttributes(attrs,
                     R.styleable.LottieAnimationView, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
             mAnimationId = a.getResourceId(R.styleable.LottieAnimationView_lottie_rawRes, 0);
             a.recycle();
         }
+        setOnPreferenceClickListener(this);
     }
 
     private void updateAnimationStatus(boolean playAnimation) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
index 4a14403..f197cbb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -22,6 +22,9 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
 
 import com.airbnb.lottie.LottieAnimationView;
 
@@ -41,14 +44,15 @@
     @Mock
     LottieAnimationView mAnimationView;
 
+    private Context mContext;
     private IllustrationPreference mPreference;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        final Context context = RuntimeEnvironment.application;
+        mContext = RuntimeEnvironment.application;
         final AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
-        mPreference = new IllustrationPreference(context, attributeSet);
+        mPreference = new IllustrationPreference(mContext, attributeSet);
         ReflectionHelpers.setField(mPreference, "mIllustrationView", mAnimationView);
     }
 
@@ -65,4 +69,27 @@
 
         assertThat(mPreference.isAnimating()).isTrue();
     }
+
+    @Test
+    public void setMiddleGroundView_middleGroundView_shouldVisible() {
+        final View view = new View(mContext);
+        final FrameLayout layout = new FrameLayout(mContext);
+        layout.setVisibility(View.GONE);
+        ReflectionHelpers.setField(mPreference, "mMiddleGroundView", view);
+        ReflectionHelpers.setField(mPreference, "mMiddleGroundLayout", layout);
+
+        mPreference.setMiddleGroundView(view);
+
+        assertThat(layout.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void enableAnimationAutoScale_shouldChangeScaleType() {
+        final LottieAnimationView animationView = new LottieAnimationView(mContext);
+        ReflectionHelpers.setField(mPreference, "mIllustrationView", animationView);
+
+        mPreference.enableAnimationAutoScale(true);
+
+        assertThat(animationView.getScaleType()).isEqualTo(ImageView.ScaleType.CENTER_CROP);
+    }
 }