Create preference widget to support Lottie illustrations.

Fixes: 186409607
Test: run robo test and see the UI
Change-Id: Ie78e6585dfe0aefeb6ffa50db3935e59010648f5
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 69cee00..a65bf41 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -54,6 +54,7 @@
         "SettingsLibAppPreference",
         "SettingsLibSearchWidget",
         "SettingsLibSettingsSpinner",
+        "SettingsLibIllustrationPreference",
         "SettingsLibLayoutPreference",
         "SettingsLibMainSwitchPreference",
         "SettingsLibActionButtonsPreference",
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
new file mode 100644
index 0000000..f8dd384
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -0,0 +1,23 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+    name: "SettingsLibIllustrationPreference",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    static_libs: [
+        "androidx.preference_preference",
+        "lottie",
+    ],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml
new file mode 100644
index 0000000..120b085
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/IllustrationPreference/res/drawable/ic_gesture_play_button.xml b/packages/SettingsLib/IllustrationPreference/res/drawable/ic_gesture_play_button.xml
new file mode 100644
index 0000000..55b3115
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/drawable/ic_gesture_play_button.xml
@@ -0,0 +1,24 @@
+<!--
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path android:fillColor="#FFFFFF" android:pathData="M24,24m-19,0a19,19 0,1 1,38 0a19,19 0,1 1,-38 0"/>
+    <path android:fillColor="#1A73E8" android:pathData="M20,33l12,-9l-12,-9z"/>
+    <path android:fillColor="#1A73E8" android:pathData="M24,4C12.96,4 4,12.96 4,24s8.96,20 20,20s20,-8.96 20,-20S35.04,4 24,4zM24,40c-8.82,0 -16,-7.18 -16,-16S15.18,8 24,8s16,7.18 16,16S32.82,40 24,40z"/>
+</vector>
diff --git a/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml b/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml
new file mode 100644
index 0000000..dd2fa5e
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+-->
+
+<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">
+        <shape android:shape="rectangle">
+            <solid android:color="@color/settingslib_protection_color"/>
+            <corners android:radius="28dp"/>
+        </shape>
+    </item>
+</layer-list>
diff --git a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
new file mode 100644
index 0000000..2cbb888
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:background="?android:attr/colorBackground"
+    android:gravity="center"
+    android:orientation="horizontal">
+
+    <View
+        android:id="@+id/protection_layer"
+        android:layout_width="412dp"
+        android:layout_height="300dp"
+        android:layout_gravity="center"
+        android:padding="@dimen/settingslib_illustration_padding"
+        android:background="@drawable/protection_background"/>
+
+    <com.airbnb.lottie.LottieAnimationView
+        android:id="@+id/lottie_view"
+        android:layout_width="412dp"
+        android:layout_height="300dp"
+        android:layout_gravity="center"
+        android:padding="@dimen/settingslib_illustration_padding"
+        android:importantForAccessibility="no"/>
+
+    <ImageView
+        android:id="@+id/video_play_button"
+        android:layout_width="36dp"
+        android:layout_height="36dp"
+        android:layout_gravity="center"
+        android:visibility="gone"
+        android:src="@drawable/ic_gesture_play_button"/>
+
+</FrameLayout>
+
diff --git a/packages/SettingsLib/IllustrationPreference/res/values-night/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values-night/colors.xml
new file mode 100644
index 0000000..71b18a8
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/values-night/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<resources>
+    <color name="settingslib_protection_color">@android:color/black</color>
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
new file mode 100644
index 0000000..e53a43e
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<resources>
+    <color name="settingslib_protection_color">@android:color/white</color>
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml b/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml
new file mode 100644
index 0000000..79562b9
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<resources>
+    <!-- Padding of illustration -->
+    <dimen name="settingslib_illustration_padding">16dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
new file mode 100644
index 0000000..90b8a32
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -0,0 +1,166 @@
+/*
+ * 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.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.PreferenceViewHolder;
+
+import com.airbnb.lottie.LottieAnimationView;
+
+/**
+ * IllustrationPreference is a preference that can play lottie format animation
+ */
+public class IllustrationPreference extends Preference implements OnPreferenceClickListener {
+
+    static final String TAG = "IllustrationPreference";
+    private int mAnimationId;
+    private boolean mIsAnimating;
+    private ImageView mPlayButton;
+    private LottieAnimationView mIllustrationView;
+
+    public IllustrationPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context, attrs);
+    }
+
+    public IllustrationPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context, attrs);
+    }
+
+    public IllustrationPreference(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init(context, attrs);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        if (mAnimationId == 0) {
+            Log.w(TAG, "Invalid illustration resource id.");
+            return;
+        }
+        mPlayButton = (ImageView) holder.findViewById(R.id.video_play_button);
+        mIllustrationView = (LottieAnimationView) holder.findViewById(R.id.lottie_view);
+        mIllustrationView.setAnimation(mAnimationId);
+        mIllustrationView.loop(true);
+        mIllustrationView.playAnimation();
+        updateAnimationStatus(mIsAnimating);
+        setOnPreferenceClickListener(this);
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        mIsAnimating = !isAnimating();
+        updateAnimationStatus(mIsAnimating);
+        return true;
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+        SavedState ss = new SavedState(superState);
+        ss.mIsAnimating = mIsAnimating;
+        return ss;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        SavedState ss = (SavedState) state;
+        super.onRestoreInstanceState(ss.getSuperState());
+        mIsAnimating = ss.mIsAnimating;
+    }
+
+    @VisibleForTesting
+    boolean isAnimating() {
+        return mIllustrationView.isAnimating();
+    }
+
+    private void init(Context context, AttributeSet attrs) {
+        setLayoutResource(R.layout.illustration_preference);
+
+        mIsAnimating = true;
+        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();
+        }
+    }
+
+    private void updateAnimationStatus(boolean playAnimation) {
+        if (playAnimation) {
+            mIllustrationView.resumeAnimation();
+            mPlayButton.setVisibility(View.INVISIBLE);
+        } else {
+            mIllustrationView.pauseAnimation();
+            mPlayButton.setVisibility(View.VISIBLE);
+        }
+    }
+
+    static class SavedState extends BaseSavedState {
+        boolean mIsAnimating;
+
+        SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        /**
+         * Constructor called from {@link #CREATOR}
+         */
+        private SavedState(Parcel in) {
+            super(in);
+            mIsAnimating = (Boolean) in.readValue(null);
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            super.writeToParcel(out, flags);
+            out.writeValue(mIsAnimating);
+        }
+
+        @Override
+        public String toString() {
+            return "IllustrationPreference.SavedState{"
+                    + Integer.toHexString(System.identityHashCode(this))
+                    + " mIsAnimating=" + mIsAnimating + "}";
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+                    public SavedState createFromParcel(Parcel in) {
+                        return new SavedState(in);
+                    }
+
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
+    }
+}
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
new file mode 100644
index 0000000..4a14403
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.airbnb.lottie.LottieAnimationView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+public class IllustrationPreferenceTest {
+
+    @Mock
+    LottieAnimationView mAnimationView;
+
+    private IllustrationPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        final Context context = RuntimeEnvironment.application;
+        final AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
+        mPreference = new IllustrationPreference(context, attributeSet);
+        ReflectionHelpers.setField(mPreference, "mIllustrationView", mAnimationView);
+    }
+
+    @Test
+    public void isAnimating_lottieAnimationViewIsNotAnimating_shouldReturnFalse() {
+        when(mAnimationView.isAnimating()).thenReturn(false);
+
+        assertThat(mPreference.isAnimating()).isFalse();
+    }
+
+    @Test
+    public void isAnimating_lottieAnimationViewIsAnimating_shouldReturnTrue() {
+        when(mAnimationView.isAnimating()).thenReturn(true);
+
+        assertThat(mPreference.isAnimating()).isTrue();
+    }
+}