diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 69cee00041ad9..a65bf41210a1b 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -54,6 +54,7 @@ java_defaults { "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 0000000000000..f8dd3849c2a17 --- /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 0000000000000..120b0859a70aa --- /dev/null +++ b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + 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 0000000000000..55b3115a58a46 --- /dev/null +++ b/packages/SettingsLib/IllustrationPreference/res/drawable/ic_gesture_play_button.xml @@ -0,0 +1,24 @@ + + + + + + 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 0000000000000..dd2fa5e0f2144 --- /dev/null +++ b/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + 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 0000000000000..2cbb888b93d39 --- /dev/null +++ b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + 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 0000000000000..71b18a8814bce --- /dev/null +++ b/packages/SettingsLib/IllustrationPreference/res/values-night/colors.xml @@ -0,0 +1,20 @@ + + + + + @android:color/black + diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml new file mode 100644 index 0000000000000..e53a43e7953e5 --- /dev/null +++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml @@ -0,0 +1,20 @@ + + + + + @android:color/white + diff --git a/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml b/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml new file mode 100644 index 0000000000000..79562b962ad1c --- /dev/null +++ b/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml @@ -0,0 +1,21 @@ + + + + + + 16dp + 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 0000000000000..90b8a3210a428 --- /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 CREATOR = + new Parcelable.Creator() { + 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 0000000000000..4a14403b7ea3f --- /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(); + } +}