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(