Refactor BottomActionBar

- BottomActionBar accept setting content view into bottomsheet.
- Wallpaper preview can reuse the existing logic of bottom sheet view, and work with BottomActionBar(ag/11261779).
- BottomActionBar can accept 1+ bottom sheet content per action button.
- Vedio:
Info button: https://drive.google.com/a/google.com/file/d/1FBbrMlRWynjeSNwV5cYRWEyUzt6xBxkM/view?usp=sharing
Two button for bottom sheet case(Demo, not implemented): https://drive.google.com/a/google.com/file/d/1ONZqdobGnmoydkGmwE6NEkAOuyUFl9T1/view?usp=sharing

Test: Manually
Fixes: 155157385
Change-Id: I2c62583e7b54a60721a7ec1fc0fb46af8f85487d
diff --git a/res/color/bottom_action_button_color_tint.xml b/res/color/bottom_action_button_color_tint.xml
new file mode 100644
index 0000000..f23e905
--- /dev/null
+++ b/res/color/bottom_action_button_color_tint.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_selected="false"
+        android:color="@color/material_grey500" />
+    <item
+        android:state_selected="true"
+        android:color="?android:colorAccent" />
+</selector>
\ No newline at end of file
diff --git a/res/layout/activity_individual_picker.xml b/res/layout/activity_individual_picker.xml
index f476c7c..c79b06d 100755
--- a/res/layout/activity_individual_picker.xml
+++ b/res/layout/activity_individual_picker.xml
@@ -14,9 +14,9 @@
      limitations under the License.
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:orientation="vertical">
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
 
     <com.google.android.material.appbar.AppBarLayout
         android:layout_width="match_parent"
@@ -32,11 +32,16 @@
     </com.google.android.material.appbar.AppBarLayout>
 
     <FrameLayout
-        android:id="@+id/fragment_container"
         android:layout_width="match_parent"
         android:layout_height="0dp"
-        android:layout_weight="1"/>
+        android:layout_weight="1">
 
-    <include layout="@layout/bottom_action_bar" />
+        <FrameLayout
+            android:id="@+id/fragment_container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginBottom="@dimen/bottom_navbar_height"/>
 
+        <include layout="@layout/bottom_action_bar" />
+    </FrameLayout>
 </LinearLayout>
diff --git a/res/layout/activity_top_level_picker.xml b/res/layout/activity_top_level_picker.xml
index 21ced7f..185cc1b 100755
--- a/res/layout/activity_top_level_picker.xml
+++ b/res/layout/activity_top_level_picker.xml
@@ -13,16 +13,15 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:layout_height="match_parent">
 
     <FrameLayout
         android:id="@+id/fragment_container"
         android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1" />
+        android:layout_height="match_parent"
+        android:layout_marginBottom="@dimen/bottom_navbar_height"/>
 
     <include layout="@layout/bottom_action_bar" />
-</LinearLayout>
+</FrameLayout>
diff --git a/res/layout/bottom_actions_layout.xml b/res/layout/bottom_actions_layout.xml
index ae56ee2..41c2e6f 100644
--- a/res/layout/bottom_actions_layout.xml
+++ b/res/layout/bottom_actions_layout.xml
@@ -23,9 +23,7 @@
     <!--  Bottom Sheet  -->
     <androidx.coordinatorlayout.widget.CoordinatorLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:clipToPadding="false"
-        android:paddingTop="2dp">
+        android:layout_height="wrap_content">
         <!-- Bottom sheet view should be a child view of CoordinatorLayout -->
         <FrameLayout
             android:id="@+id/action_bottom_sheet"
@@ -34,11 +32,9 @@
             android:background="@drawable/bottom_sheet_background"
             android:theme="@style/WallpaperPicker.BottomPaneStyle"
             android:elevation="@dimen/bottom_action_bar_elevation"
-            android:visibility="gone"
+            android:layout_marginTop="@dimen/bottom_action_bar_bottom_sheet_margin_top"
             app:behavior_peekHeight="@dimen/preview_attribution_pane_collapsed_height"
-            app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
-            <include layout="@layout/bottom_action_bar_preview_info"/>
-        </FrameLayout>
+            app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"/>
     </androidx.coordinatorlayout.widget.CoordinatorLayout>
 
     <!--  Bottom Tabs  -->
@@ -60,6 +56,7 @@
             android:src="@drawable/ic_close_gm2_24px"
             android:background="?android:attr/selectableItemBackgroundBorderless"
             android:contentDescription="@string/bottom_action_bar_back"
+            android:tint="@color/bottom_action_button_color_tint"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintEnd_toStartOf="@id/action_rotation"
             app:layout_constraintHorizontal_chainStyle="spread_inside"
@@ -73,6 +70,7 @@
             android:src="@drawable/ic_slideshow_24dp"
             android:background="?android:attr/selectableItemBackgroundBorderless"
             android:contentDescription="@string/bottom_action_bar_slideshow_wallpaper"
+            android:tint="@color/bottom_action_button_color_tint"
             android:visibility="gone"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintEnd_toStartOf="@id/action_delete"
@@ -86,6 +84,7 @@
             android:src="@drawable/ic_delete_24px"
             android:background="?android:attr/selectableItemBackgroundBorderless"
             android:contentDescription="@string/delete_live_wallpaper"
+            android:tint="@color/bottom_action_button_color_tint"
             android:visibility="gone"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintEnd_toStartOf="@id/action_information"
@@ -99,6 +98,7 @@
             android:src="@drawable/ic_info_gm2_24px"
             android:background="?android:attr/selectableItemBackgroundBorderless"
             android:contentDescription="@string/tab_info"
+            android:tint="@color/bottom_action_button_color_tint"
             android:visibility="gone"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintEnd_toStartOf="@id/action_edit"
@@ -112,6 +112,7 @@
             android:src="@drawable/ic_pan_zoom_24dp"
             android:background="?android:attr/selectableItemBackgroundBorderless"
             android:contentDescription="@string/bottom_action_bar_edit"
+            android:tint="@color/bottom_action_button_color_tint"
             android:visibility="gone"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintEnd_toStartOf="@id/action_customize"
@@ -125,6 +126,7 @@
             android:src="@drawable/ic_tune_black_24dp"
             android:background="?android:attr/selectableItemBackgroundBorderless"
             android:contentDescription="@string/tab_customize"
+            android:tint="@color/bottom_action_button_color_tint"
             android:visibility="gone"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintEnd_toStartOf="@id/action_download"
@@ -138,6 +140,7 @@
             android:src="@drawable/ic_file_download_gm2_24px"
             android:background="?android:attr/selectableItemBackgroundBorderless"
             android:contentDescription="@string/bottom_action_bar_download"
+            android:tint="@color/bottom_action_button_color_tint"
             android:visibility="gone"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintEnd_toStartOf="@id/action_progress"
@@ -161,6 +164,7 @@
             android:src="@drawable/ic_done_gm2_24px"
             android:background="?android:attr/selectableItemBackgroundBorderless"
             android:contentDescription="@string/bottom_action_bar_apply"
+            android:tint="@color/bottom_action_button_color_tint"
             android:visibility="gone"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
diff --git a/res/layout/bottom_action_bar_preview_info.xml b/res/layout/wallpaper_info_view.xml
similarity index 86%
rename from res/layout/bottom_action_bar_preview_info.xml
rename to res/layout/wallpaper_info_view.xml
index 62715c2..ad78322 100644
--- a/res/layout/bottom_action_bar_preview_info.xml
+++ b/res/layout/wallpaper_info_view.xml
@@ -15,8 +15,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/page_info"
+<com.android.wallpaper.widget.WallpaperInfoView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/wallpaper_info"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
     android:orientation="vertical"
@@ -25,7 +26,7 @@
     android:theme="@style/WallpaperPicker.BottomPaneStyle">
 
   <TextView
-      android:id="@+id/preview_attribution_pane_title"
+      android:id="@+id/wallpaper_info_title"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:minHeight="@dimen/preview_attribution_pane_title_height"
@@ -37,7 +38,7 @@
       android:textColor="@color/action_bar_bottom_sheet_text_color"/>
 
   <TextView
-      android:id="@+id/preview_attribution_pane_subtitle1"
+      android:id="@+id/wallpaper_info_subtitle1"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:gravity="center"
@@ -48,7 +49,7 @@
       android:visibility="gone"/>
 
   <TextView
-      android:id="@+id/preview_attribution_pane_subtitle2"
+      android:id="@+id/wallpaper_info_subtitle2"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:gravity="center_horizontal"
@@ -57,4 +58,4 @@
       android:lineHeight="16dp"
       android:textColor="@color/action_bar_bottom_sheet_text_color"
       android:visibility="gone"/>
-</LinearLayout>
\ No newline at end of file
+</com.android.wallpaper.widget.WallpaperInfoView>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 8ba3a62..9e248cc 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -221,6 +221,7 @@
     <dimen name="bottom_action_button_size">48dp</dimen>
     <dimen name="bottom_action_button_padding">12dp</dimen>
     <dimen name="bottom_action_icon_size">24dp</dimen>
+    <dimen name="bottom_action_bar_bottom_sheet_margin_top">2dp</dimen>
 
     <dimen name="option_border_width">2dp</dimen>
     <dimen name="option_selected_border_width">3dp</dimen>
diff --git a/src/com/android/wallpaper/picker/LivePreviewFragment.java b/src/com/android/wallpaper/picker/LivePreviewFragment.java
index 579d9ee..3b2b424 100644
--- a/src/com/android/wallpaper/picker/LivePreviewFragment.java
+++ b/src/com/android/wallpaper/picker/LivePreviewFragment.java
@@ -71,6 +71,7 @@
 import com.android.wallpaper.util.WallpaperConnection;
 import com.android.wallpaper.widget.BottomActionBar;
 import com.android.wallpaper.widget.LiveTileOverlay;
+import com.android.wallpaper.widget.WallpaperInfoView;
 
 import com.google.android.material.tabs.TabLayout;
 
@@ -113,6 +114,7 @@
     private InfoPageController mInfoPageController;
     private ImageView mHomePreview;
     private BottomActionBar mBottomActionBar;
+    private WallpaperInfoView mWallpaperInfoView;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -338,9 +340,11 @@
                                         mLoadingProgressBar.hide();
                                     }
                                     mLoadingScrim.setVisibility(View.GONE);
-                                    mBottomActionBar.populateInfoPage(
-                                            mWallpaper.getAttributions(getContext()),
-                                            shouldShowMetadataInPreview());
+                                    if (mWallpaperInfoView != null) {
+                                        mWallpaperInfoView.populateWallpaperInfo(
+                                                mWallpaper.getAttributions(getContext()),
+                                                shouldShowMetadataInPreview());
+                                    }
                                 }));
                         final Drawable placeholder = previewView.getDrawable() == null
                                 ? new ColorDrawable(getResources().getColor(R.color.secondary_color,
@@ -379,6 +383,9 @@
             mBottomActionBar.showActionsOnly(INFORMATION, CUSTOMIZE, APPLY);
             mBottomActionBar.setActionClickListener(APPLY, unused ->
                     this.onSetWallpaperClicked(null));
+            mWallpaperInfoView =
+                    (WallpaperInfoView) mBottomActionBar.inflateViewToBottomSheetAndBindAction(
+                            R.layout.wallpaper_info_view, R.id.wallpaper_info, INFORMATION);
             mBottomActionBar.show();
         }
     }
diff --git a/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java
index 58cb84f..0f042ab 100755
--- a/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java
+++ b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java
@@ -89,6 +89,7 @@
 import com.android.wallpaper.util.DiskBasedLogger;
 import com.android.wallpaper.util.TileSizeCalculator;
 import com.android.wallpaper.widget.BottomActionBar;
+import com.android.wallpaper.widget.WallpaperInfoView;
 
 import com.bumptech.glide.Glide;
 import com.bumptech.glide.MemoryCategory;
@@ -207,6 +208,7 @@
     };
     PackageStatusNotifier.Listener mAppStatusListener;
     BottomActionBar mBottomActionBar;
+    WallpaperInfoView mWallpaperInfoView;
     @Nullable WallpaperInfo mSelectedWallpaperInfo;
 
     private ProgressDialog mProgressDialog;
@@ -549,6 +551,11 @@
                 mWallpaperSetter.requestDestination(getActivity(), getFragmentManager(), this,
                         mSelectedWallpaperInfo instanceof LiveWallpaperInfo);
             });
+
+            mWallpaperInfoView =
+                    (WallpaperInfoView) mBottomActionBar.inflateViewToBottomSheetAndBindAction(
+                            R.layout.wallpaper_info_view, R.id.wallpaper_info, INFORMATION);
+
             mBottomActionBar.show();
         }
     }
@@ -941,8 +948,8 @@
         updateBottomActions(mSelectedWallpaperInfo != null);
         updateThumbnail(mSelectedWallpaperInfo);
         // Populate wallpaper info to bottom sheet page.
-        if (mSelectedWallpaperInfo != null) {
-            mBottomActionBar.populateInfoPage(
+        if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
+            mWallpaperInfoView.populateWallpaperInfo(
                     mSelectedWallpaperInfo.getAttributions(getContext()),
                     shouldShowMetadataInPreview(mSelectedWallpaperInfo));
         }
diff --git a/src/com/android/wallpaper/widget/BottomActionBar.java b/src/com/android/wallpaper/widget/BottomActionBar.java
index bbe9c7d..8893c32 100644
--- a/src/com/android/wallpaper/widget/BottomActionBar.java
+++ b/src/com/android/wallpaper/widget/BottomActionBar.java
@@ -26,9 +26,9 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
 
+import androidx.annotation.IdRes;
+import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
@@ -40,7 +40,6 @@
 import java.util.Arrays;
 import java.util.EnumMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -62,20 +61,21 @@
     }
 
     private final Map<BottomAction, View> mActionMap = new EnumMap<>(BottomAction.class);
-    private final ViewGroup mBottomSheet;
+    private final Map<BottomAction, View> mContentViewMap = new EnumMap<>(BottomAction.class);
+
+    private final ViewGroup mBottomSheetView;
     private final BottomSheetBehavior<ViewGroup> mBottomSheetBehavior;
-    private final TextView mAttributionTitle;
-    private final TextView mAttributionSubtitle1;
-    private final TextView mAttributionSubtitle2;
+
+    /**
+     * For updating the selected state of expanding bottom sheet, the corresponding action button
+     * will be set to selected state.
+     */
+    private BottomAction mSelectedAction;
 
     public BottomActionBar(@NonNull Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
         LayoutInflater.from(context).inflate(R.layout.bottom_actions_layout, this, true);
 
-        mAttributionTitle = findViewById(R.id.preview_attribution_pane_title);
-        mAttributionSubtitle1 = findViewById(R.id.preview_attribution_pane_subtitle1);
-        mAttributionSubtitle2 = findViewById(R.id.preview_attribution_pane_subtitle2);
-
         mActionMap.put(BottomAction.ROTATION, findViewById(R.id.action_rotation));
         mActionMap.put(BottomAction.DELETE, findViewById(R.id.action_delete));
         mActionMap.put(BottomAction.INFORMATION, findViewById(R.id.action_information));
@@ -85,32 +85,30 @@
         mActionMap.put(BottomAction.PROGRESS, findViewById(R.id.action_progress));
         mActionMap.put(BottomAction.APPLY, findViewById(R.id.action_apply));
 
-        mBottomSheet = findViewById(R.id.action_bottom_sheet);
-        mBottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);
-        mBottomSheetBehavior.setState(STATE_COLLAPSED);
-
+        mBottomSheetView = findViewById(R.id.action_bottom_sheet);
         // Workaround as we don't have access to bottomDialogCornerRadius, mBottomSheet radii are
         // set to dialogCornerRadius by default.
-        GradientDrawable bottomSheetBackground = (GradientDrawable) mBottomSheet.getBackground();
-        float[] radii = bottomSheetBackground.getCornerRadii();
+        GradientDrawable background = (GradientDrawable) mBottomSheetView.getBackground();
+        float[] radii = background.getCornerRadii();
         for (int i = 0; i < radii.length; i++) {
-            radii[i]*=2f;
+            radii[i] *= 2f;
         }
-        bottomSheetBackground = ((GradientDrawable)bottomSheetBackground.mutate());
-        bottomSheetBackground.setCornerRadii(radii);
-        mBottomSheet.setBackground(bottomSheetBackground);
+        background = ((GradientDrawable) background.mutate());
+        background.setCornerRadii(radii);
+        mBottomSheetView.setBackground(background);
 
-        ImageView informationIcon = findViewById(R.id.action_information);
+        mBottomSheetBehavior = BottomSheetBehavior.from(mBottomSheetView);
+        mBottomSheetBehavior.setState(STATE_COLLAPSED);
         mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetCallback() {
             @Override
             public void onStateChanged(@NonNull View bottomSheet, int newState) {
                 if (newState == STATE_COLLAPSED) {
-                    informationIcon.setColorFilter(getContext().getColor(R.color.material_grey500));
+                    updateSelectedState(mSelectedAction, /* selected= */ false);
+                    mSelectedAction = null;
                 } else if (newState == STATE_EXPANDED) {
-                    informationIcon.setColorFilter(getContext().getColor(R.color.accent_color));
+                    updateSelectedState(mSelectedAction, /* selected= */ true);
                 }
             }
-
             @Override
             public void onSlide(@NonNull View bottomSheet, float slideOffset) {}
         });
@@ -125,6 +123,44 @@
     }
 
     /**
+     * Inflates a content view to the bottom sheet, and binds with a {@code BottomAction} to
+     * expand/collapse the bottom sheet.
+     *
+     * @param contentLayoutId the layout res to be inflected on the bottom sheet
+     * @param contentViewId the view id of the inflected content view
+     * @param action the action button to be bound to expand/collapse the bottom sheet
+     * @return the view of {@param contentViewId}
+     */
+    public View inflateViewToBottomSheetAndBindAction(
+            @LayoutRes int contentLayoutId, @IdRes int contentViewId, BottomAction action) {
+        View contentView = LayoutInflater
+                .from(getContext())
+                .inflate(contentLayoutId, mBottomSheetView)
+                .findViewById(contentViewId);
+        contentView.setVisibility(GONE);
+        mContentViewMap.put(action, contentView);
+
+        setActionClickListener(action, unused -> {
+            mContentViewMap.forEach((a, v) -> v.setVisibility(a.equals(action) ? VISIBLE : GONE));
+            BottomAction previousHighlightButton = mSelectedAction;
+            mSelectedAction = action;
+            // If the bottom sheet is expanding with a highlight button, then clicking another
+            // action button to show bottom sheet will only update the content for expanding bottom
+            // sheet, and update the highlight button.
+            if (previousHighlightButton != null && !action.equals(previousHighlightButton)) {
+                updateSelectedState(previousHighlightButton, /* selected= */ false);
+                updateSelectedState(mSelectedAction, /* selected= */ true);
+                return;
+            }
+            mBottomSheetBehavior.setState(mBottomSheetBehavior.getState() == STATE_COLLAPSED
+                    ? STATE_EXPANDED
+                    : STATE_COLLAPSED);
+        });
+
+        return contentView;
+    }
+
+    /**
      * Sets the click listener to the specific action.
      *
      * @param bottomAction the specific action
@@ -132,7 +168,12 @@
      */
     public void setActionClickListener(
             BottomAction bottomAction, OnClickListener actionClickListener) {
-        mActionMap.get(bottomAction).setOnClickListener(actionClickListener);
+        View buttonView = mActionMap.get(bottomAction);
+        if (buttonView.hasOnClickListeners()) {
+            throw new IllegalStateException(
+                    "Had already set a click listener to button: " + bottomAction);
+        }
+        buttonView.setOnClickListener(actionClickListener);
     }
 
     /** Binds the cancel button to back key. */
@@ -140,50 +181,6 @@
         findViewById(R.id.action_back).setOnClickListener(v -> activity.onBackPressed());
     }
 
-    /** Clears all the actions' click listeners */
-    public void clearActionClickListeners() {
-        mActionMap.forEach((bottomAction, view) -> view.setOnClickListener(null));
-        findViewById(R.id.action_back).setOnClickListener(null);
-    }
-
-    /**
-     * Populates attributions(wallpaper info) to the information page.
-     *
-     * <p>Once get called, the {@link OnClickListener} to show/hide the information page will be
-     * set for the {@code BottomAction.INFORMATION}.
-     */
-    public void populateInfoPage(List<String> attributions, boolean showMetadata) {
-        resetInfoPage();
-
-        // Ensure the ClickListener can work normally if has info been populated, since it could be
-        // removed by #clearActionClickListeners.
-        setActionClickListener(BottomAction.INFORMATION, unused -> {
-            mBottomSheet.setVisibility(mBottomSheetBehavior.getState() == STATE_COLLAPSED
-                    ? VISIBLE
-                    : GONE);
-            mBottomSheetBehavior.setState(mBottomSheetBehavior.getState() == STATE_COLLAPSED
-                    ? STATE_EXPANDED
-                    : STATE_COLLAPSED
-            );
-        });
-
-        if (attributions.size() > 0 && attributions.get(0) != null) {
-            mAttributionTitle.setText(attributions.get(0));
-        }
-
-        if (showMetadata) {
-            if (attributions.size() > 1 && attributions.get(1) != null) {
-                mAttributionSubtitle1.setVisibility(View.VISIBLE);
-                mAttributionSubtitle1.setText(attributions.get(1));
-            }
-
-            if (attributions.size() > 2 && attributions.get(2) != null) {
-                mAttributionSubtitle2.setVisibility(View.VISIBLE);
-                mAttributionSubtitle2.setText(attributions.get(2));
-            }
-        }
-    }
-
     /** Returns {@code true} if visible. */
     public boolean isVisible() {
         return getVisibility() == VISIBLE;
@@ -219,7 +216,7 @@
         for (BottomAction action : actions) {
             mActionMap.get(action).setVisibility(GONE);
 
-            if (BottomAction.INFORMATION.equals(action)) {
+            if (action.equals(mSelectedAction)) {
                 mBottomSheetBehavior.setState(STATE_COLLAPSED);
             }
         }
@@ -251,12 +248,12 @@
 
     /** Enables all the actions' {@link View}. */
     public void enableActions() {
-        mActionMap.forEach((bottomAction, view) -> view.setEnabled(true));
+        enableActions(BottomAction.values());
     }
 
     /** Disables all the actions' {@link View}. */
     public void disableActions() {
-        mActionMap.forEach((bottomAction, view) -> view.setEnabled(false));
+        disableActions(BottomAction.values());
     }
 
     /**
@@ -287,16 +284,19 @@
         hideAllActions();
         clearActionClickListeners();
         enableActions();
-        resetInfoPage();
+        mActionMap.forEach(
+                (action, view) -> updateSelectedState(action, /* selected= */ false));
+        mBottomSheetView.removeAllViews();
+        mContentViewMap.clear();
     }
 
-    private void resetInfoPage() {
-        mAttributionTitle.setText(null);
+    /** Clears all the actions' click listeners */
+    private void clearActionClickListeners() {
+        mActionMap.forEach((bottomAction, view) -> view.setOnClickListener(null));
+        findViewById(R.id.action_back).setOnClickListener(null);
+    }
 
-        mAttributionSubtitle1.setText(null);
-        mAttributionSubtitle1.setVisibility(GONE);
-
-        mAttributionSubtitle2.setText(null);
-        mAttributionSubtitle2.setVisibility(GONE);
+    private void updateSelectedState(BottomAction action, boolean selected) {
+        mActionMap.get(action).setSelected(selected);
     }
 }
diff --git a/src/com/android/wallpaper/widget/WallpaperInfoView.java b/src/com/android/wallpaper/widget/WallpaperInfoView.java
new file mode 100644
index 0000000..c3b75de
--- /dev/null
+++ b/src/com/android/wallpaper/widget/WallpaperInfoView.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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.wallpaper.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.wallpaper.R;
+
+import java.util.List;
+
+/** A view for displaying wallpaper info. */
+public class WallpaperInfoView extends LinearLayout {
+
+    private TextView mTitle;
+    private TextView mSubtitle1;
+    private TextView mSubtitle2;
+
+    public WallpaperInfoView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mTitle = findViewById(R.id.wallpaper_info_title);
+        mSubtitle1 = findViewById(R.id.wallpaper_info_subtitle1);
+        mSubtitle2 = findViewById(R.id.wallpaper_info_subtitle2);
+    }
+
+    /** Populates wallpaper info. */
+    public void populateWallpaperInfo(List<String> attributions, boolean showMetadata) {
+        // Reset
+        mTitle.setText(null);
+        mSubtitle1.setText(null);
+        mSubtitle1.setVisibility(View.GONE);
+        mSubtitle2.setText(null);
+        mSubtitle2.setVisibility(View.GONE);
+
+        if (attributions.size() > 0 && attributions.get(0) != null) {
+            mTitle.setText(attributions.get(0));
+        }
+
+        if (showMetadata) {
+            if (attributions.size() > 1 && attributions.get(1) != null) {
+                mSubtitle1.setVisibility(View.VISIBLE);
+                mSubtitle1.setText(attributions.get(1));
+            }
+
+            if (attributions.size() > 2 && attributions.get(2) != null) {
+                mSubtitle2.setVisibility(View.VISIBLE);
+                mSubtitle2.setText(attributions.get(2));
+            }
+        }
+    }
+}