Auto hide IllustrationPreference when lottie file is empty
Flag: com.android.settingslib.widget.flags.auto_hide_lottie_res
Bug: 337873972
Test: unit test
Test: manual: delete `lottie_adaptive_brightness.json` from overlay, navigate to Display > Adaptive Brightness -- verify no empty container
Change-Id: I873976d3e87b119a3ee690d95c68149f249fe7a9
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
index 6407810..c3a91a2 100644
--- a/packages/SettingsLib/IllustrationPreference/Android.bp
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -21,6 +21,7 @@
"SettingsLibColor",
"androidx.preference_preference",
"lottie",
+ "settingslib_illustrationpreference_flags_lib",
],
sdk_version: "system_current",
@@ -31,3 +32,24 @@
"com.android.permission",
],
}
+
+aconfig_declarations {
+ name: "settingslib_illustrationpreference_flags",
+ package: "com.android.settingslib.widget.flags",
+ container: "system",
+ srcs: [
+ "aconfig/illustrationpreference.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "settingslib_illustrationpreference_flags_lib",
+ aconfig_declarations: "settingslib_illustrationpreference_flags",
+
+ min_sdk_version: "30",
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
+}
diff --git a/packages/SettingsLib/IllustrationPreference/aconfig/illustrationpreference.aconfig b/packages/SettingsLib/IllustrationPreference/aconfig/illustrationpreference.aconfig
new file mode 100644
index 0000000..e566d89
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/aconfig/illustrationpreference.aconfig
@@ -0,0 +1,12 @@
+package: "com.android.settingslib.widget.flags"
+container: "system"
+
+flag {
+ name: "auto_hide_empty_lottie_res"
+ namespace: "android_settings"
+ description: "Hides IllustrationPreference when Lottie resource is an empty file"
+ bug: "337873972"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
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 815a101..bbf0315 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -39,12 +39,14 @@
import androidx.preference.PreferenceViewHolder;
import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
+import com.android.settingslib.widget.flags.Flags;
import com.android.settingslib.widget.preference.illustration.R;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieDrawable;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.InputStream;
/**
@@ -142,7 +144,7 @@
illustrationFrame.setLayoutParams(lp);
illustrationView.setCacheComposition(mCacheComposition);
- handleImageWithAnimation(illustrationView);
+ handleImageWithAnimation(illustrationView, illustrationFrame);
handleImageFrameMaxHeight(backgroundView, illustrationView);
if (mIsAutoScale) {
@@ -332,7 +334,8 @@
}
}
- private void handleImageWithAnimation(LottieAnimationView illustrationView) {
+ private void handleImageWithAnimation(LottieAnimationView illustrationView,
+ ViewGroup container) {
if (mImageDrawable != null) {
resetAnimations(illustrationView);
illustrationView.setImageDrawable(mImageDrawable);
@@ -356,6 +359,25 @@
}
if (mImageResId > 0) {
+ if (Flags.autoHideEmptyLottieRes()) {
+ // Check if resource is empty
+ try (InputStream is = illustrationView.getResources()
+ .openRawResource(mImageResId)) {
+ int check = is.read();
+ // -1 = end of stream. if first read is end of stream, then file is empty
+ if (check == -1) {
+ illustrationView.setVisibility(View.GONE);
+ container.setVisibility(View.GONE);
+ return;
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to open Lottie raw resource", e);
+ }
+
+ illustrationView.setVisibility(View.VISIBLE);
+ container.setVisibility(View.VISIBLE);
+ }
+
resetAnimations(illustrationView);
illustrationView.setImageResource(mImageResId);
final Drawable drawable = illustrationView.getDrawable();
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index f4ddd0a..f87b519 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -51,6 +51,7 @@
"androidx.core_core",
"flag-junit",
"settingslib_media_flags_lib",
+ "settingslib_illustrationpreference_flags_lib",
"testng", // TODO: remove once JUnit on Android provides assertThrows
],
java_resource_dirs: ["config"],
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 6590bbd..ca53fc2 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
@@ -26,10 +26,14 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.AnimationDrawable;
import android.net.Uri;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -39,11 +43,13 @@
import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
+import com.android.settingslib.widget.flags.Flags;
import com.android.settingslib.widget.preference.illustration.R;
import com.airbnb.lottie.LottieAnimationView;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -51,10 +57,14 @@
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
+import java.io.ByteArrayInputStream;
+
@RunWith(RobolectricTestRunner.class)
public class IllustrationPreferenceTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock
private ViewGroup mRootView;
private Uri mImageUri;
@@ -66,6 +76,7 @@
private final Context mContext = ApplicationProvider.getApplicationContext();
private IllustrationPreference.OnBindListener mOnBindListener;
private LottieAnimationView mOnBindListenerAnimationView;
+ private FrameLayout mIllustrationFrame;
@Before
public void setUp() {
@@ -75,14 +86,14 @@
mBackgroundView = new ImageView(mContext);
mAnimationView = spy(new LottieAnimationView(mContext));
mMiddleGroundLayout = new FrameLayout(mContext);
- final FrameLayout illustrationFrame = new FrameLayout(mContext);
- illustrationFrame.setLayoutParams(
+ mIllustrationFrame = new FrameLayout(mContext);
+ mIllustrationFrame.setLayoutParams(
new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
doReturn(mMiddleGroundLayout).when(mRootView).findViewById(R.id.middleground_layout);
doReturn(mBackgroundView).when(mRootView).findViewById(R.id.background_view);
doReturn(mAnimationView).when(mRootView).findViewById(R.id.lottie_view);
- doReturn(illustrationFrame).when(mRootView).findViewById(R.id.illustration_frame);
+ doReturn(mIllustrationFrame).when(mRootView).findViewById(R.id.illustration_frame);
mViewHolder = spy(PreferenceViewHolder.createInstanceForTests(mRootView));
final AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
@@ -158,11 +169,13 @@
}
@Test
+ @DisableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
public void playLottieAnimationWithResource_verifyFailureListener() {
// fake the valid lottie image
final int fakeValidResId = 111;
doNothing().when(mAnimationView).setImageResource(fakeValidResId);
doReturn(null).when(mAnimationView).getDrawable();
+ doNothing().when(mAnimationView).setAnimation(fakeValidResId);
mPreference.setLottieAnimationResId(fakeValidResId);
mPreference.onBindViewHolder(mViewHolder);
@@ -171,6 +184,50 @@
}
@Test
+ @DisableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
+ public void handleImageWithAnimation_emptyInputStreamDisabledFlag_verifyContainerVisible() {
+ doNothing().when(mAnimationView).setImageResource(111);
+ doReturn(null).when(mAnimationView).getDrawable();
+
+ mPreference.setLottieAnimationResId(111);
+ mPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mAnimationView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mIllustrationFrame.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
+ public void handleImageWithAnimation_emptyInputStreamEnabledFlag_verifyContainerHidden() {
+ Resources res = spy(mContext.getResources());
+ doReturn(res).when(mAnimationView).getResources();
+ doReturn(new ByteArrayInputStream(new byte[] {})).when(res).openRawResource(111);
+
+ mPreference.setLottieAnimationResId(111);
+ mPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mAnimationView.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mIllustrationFrame.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
+ public void handleImageWithAnimation_nonEmptyInputStreamEnabledFlag_verifyContainerVisible() {
+ Resources res = spy(mContext.getResources());
+ doReturn(res).when(mAnimationView).getResources();
+ doReturn(new ByteArrayInputStream(new byte[] { 1, 2, 3 })).when(res).openRawResource(111);
+ doNothing().when(mAnimationView).setImageResource(111);
+ doNothing().when(mAnimationView).setAnimation(111);
+ doReturn(null).when(mAnimationView).getDrawable();
+
+ mPreference.setLottieAnimationResId(111);
+ mPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mAnimationView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mIllustrationFrame.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
public void setMaxHeight_smallerThanRestrictedHeight_matchResult() {
final int restrictedHeight =
mContext.getResources().getDimensionPixelSize(