[Live preview 1/3] Live wallpaper preview implementation

Implement live wallpaper preview in LivePreviewFragment.

Bug: 151285788
Change-Id: I6c8c450a189c6cc0d4681c0a7a378ef14d32cc6e
diff --git a/res/layout/bottom_actions_layout.xml b/res/layout/bottom_actions_layout.xml
index 15718c0..ae56ee2 100644
--- a/res/layout/bottom_actions_layout.xml
+++ b/res/layout/bottom_actions_layout.xml
@@ -34,6 +34,7 @@
             android:background="@drawable/bottom_sheet_background"
             android:theme="@style/WallpaperPicker.BottomPaneStyle"
             android:elevation="@dimen/bottom_action_bar_elevation"
+            android:visibility="gone"
             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"/>
diff --git a/res/layout/fragment_live_preview_v2.xml b/res/layout/fragment_live_preview_v2.xml
new file mode 100644
index 0000000..8220c1b
--- /dev/null
+++ b/res/layout/fragment_live_preview_v2.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="?android:colorPrimary"
+    android:orientation="vertical">
+
+    <include layout="@layout/section_header" />
+
+    <androidx.coordinatorlayout.widget.CoordinatorLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:background="?android:colorPrimary">
+
+        <com.android.wallpaper.widget.WallpaperPreviewCard
+            android:id="@+id/live_wallpaper_preview"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/live_preview_card_height"
+            android:background="@color/preview_pager_background"
+            android:paddingTop="@dimen/live_preview_card_padding_top"
+            android:paddingBottom="@dimen/live_preview_card_padding_bottom" />
+
+        <include
+            android:id="@+id/permission_needed"
+            layout="@layout/grid_item_permission_needed"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/live_preview_card_height"
+            android:paddingTop="@dimen/live_preview_card_padding_top"
+            android:paddingBottom="@dimen/live_preview_card_padding_bottom"
+            android:visibility="gone" />
+    </androidx.coordinatorlayout.widget.CoordinatorLayout>
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:fitsSystemWindows="true"
+        android:visibility="gone">
+
+        <androidx.coordinatorlayout.widget.CoordinatorLayout
+            android:id="@+id/coordinator_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom">
+
+            <LinearLayout
+                android:id="@+id/bottom_sheet"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:background="@drawable/preview_bottom_sheet_background"
+                android:orientation="vertical"
+                android:theme="@style/WallpaperPicker.BottomPaneStyle"
+                app:behavior_peekHeight="@dimen/preview_attribution_pane_collapsed_height"
+                app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
+
+                <com.google.android.material.tabs.TabLayout
+                    android:id="@+id/tablayout"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:visibility="gone"
+                    app:tabIndicatorColor="?android:attr/textColorPrimary"
+                    app:tabTextAppearance="@style/WallpaperPicker.Preview.TextAppearance.NoAllCaps" />
+
+                <com.android.wallpaper.widget.ConstraintViewPager
+                    android:id="@+id/viewpager"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+            </LinearLayout>
+
+        </androidx.coordinatorlayout.widget.CoordinatorLayout>
+    </FrameLayout>
+
+    <FrameLayout
+        android:id="@+id/loading"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:forceHasOverlappingRendering="false">
+
+        <androidx.core.widget.ContentLoadingProgressBar
+            android:id="@+id/loading_indicator"
+            style="@android:style/Widget.DeviceDefault.ProgressBar"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:indeterminate="true" />
+
+    </FrameLayout>
+
+    <include layout="@layout/bottom_action_bar" />
+
+</LinearLayout>
diff --git a/res/layout/wallpaper_preview_card_layout.xml b/res/layout/wallpaper_preview_card_layout.xml
new file mode 100644
index 0000000..0865f84
--- /dev/null
+++ b/res/layout/wallpaper_preview_card_layout.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+     Copyright (C) 2018 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <include
+        android:id="@+id/wallpaper_full_preview_card"
+        layout="@layout/wallpaper_preview_card" />
+</LinearLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 618c823..8ba3a62 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -207,6 +207,11 @@
     <!-- The maximum height ratio of PreviewPager and its parent view. -->
     <item name="preview_pager_maximum_height_ratio" format="float" type="dimen">0.7</item>
 
+    <!-- Dimensions for live wallpaper preview -->
+    <dimen name="live_preview_card_padding_top">24dp</dimen>
+    <dimen name="live_preview_card_padding_bottom">24dp</dimen>
+    <dimen name="live_preview_card_height">690dp</dimen>
+
     <!-- Dimensions for the bottom bar. -->
     <dimen name="bottom_navbar_height">76dp</dimen>
     <dimen name="bottom_action_bar_padding_horizontal">4dp</dimen>
diff --git a/src/com/android/wallpaper/picker/LivePreviewFragment.java b/src/com/android/wallpaper/picker/LivePreviewFragment.java
index 6e2c1ba..579d9ee 100644
--- a/src/com/android/wallpaper/picker/LivePreviewFragment.java
+++ b/src/com/android/wallpaper/picker/LivePreviewFragment.java
@@ -15,6 +15,10 @@
  */
 package com.android.wallpaper.picker;
 
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.CUSTOMIZE;
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
+
 import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.AlertDialog;
@@ -27,6 +31,10 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.service.wallpaper.IWallpaperConnection;
@@ -43,9 +51,11 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.cardview.widget.CardView;
 import androidx.lifecycle.LiveData;
 import androidx.slice.Slice;
 import androidx.slice.widget.SliceLiveData;
@@ -57,7 +67,10 @@
 import com.android.wallpaper.compat.BuildCompat;
 import com.android.wallpaper.model.LiveWallpaperInfo;
 import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
+import com.android.wallpaper.util.TileSizeCalculator;
 import com.android.wallpaper.util.WallpaperConnection;
+import com.android.wallpaper.widget.BottomActionBar;
+import com.android.wallpaper.widget.LiveTileOverlay;
 
 import com.google.android.material.tabs.TabLayout;
 
@@ -83,6 +96,10 @@
      */
     protected WallpaperConnection mWallpaperConnection;
 
+    private final int[] mLivePreviewLocation = new int[2];
+    private final Rect mPreviewLocalRect = new Rect();
+    private final Rect mPreviewGlobalRect = new Rect();
+
     private Intent mWallpaperIntent;
     private Intent mDeleteIntent;
     private Intent mSettingsIntent;
@@ -94,6 +111,8 @@
     private LiveData<Slice> mSettingsLiveData;
     private View mLoadingScrim;
     private InfoPageController mInfoPageController;
+    private ImageView mHomePreview;
+    private BottomActionBar mBottomActionBar;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -151,17 +170,45 @@
         mLoadingScrim = view.findViewById(R.id.loading);
         setUpLoadingIndicator();
 
-        mWallpaperConnection = new WallpaperConnection(mWallpaperIntent, activity,
-                this, null);
-        container.post(() -> {
-            if (!mWallpaperConnection.connect()) {
-                mWallpaperConnection = null;
-            }
-        });
+        if (USE_NEW_UI) {
+            ViewGroup viewGroup = view.findViewById(R.id.live_wallpaper_preview);
+            CardView homePreviewCard = viewGroup.findViewById(R.id.wallpaper_full_preview_card);
+            mHomePreview = homePreviewCard.findViewById(R.id.wallpaper_preview_image);
+            view.addOnLayoutChangeListener((thisView, left, top, right, bottom,
+                    oldLeft, oldTop, oldRight, oldBottom) ->
+                    ((CardView) mHomePreview.getParent())
+                            .setRadius(TileSizeCalculator.getPreviewCornerRadius(
+                                    getActivity(), homePreviewCard.getMeasuredWidth()))
+            );
+            // TODO(chriscsli): Integrate SurfaceView utilities of home screen
+            setupCurrentWallpaperPreview(view);
+            previewLiveWallpaper(container, mHomePreview);
+            mBottomActionBar = view.findViewById(R.id.bottom_actionbar);
+            onBottomActionBarReady(mBottomActionBar);
+        } else {
+            mWallpaperConnection = new WallpaperConnection(mWallpaperIntent, activity,
+                    this, null);
+            container.post(() -> {
+                if (!mWallpaperConnection.connect()) {
+                    mWallpaperConnection = null;
+                }
+            });
+        }
 
         return view;
     }
 
+    private void setupCurrentWallpaperPreview(View view) {
+        showCurrentWallpaper(view, /* show= */ true);
+    }
+
+    private void showCurrentWallpaper(View rootView, boolean show) {
+        rootView.findViewById(R.id.live_wallpaper_preview)
+                .setVisibility(show ? View.VISIBLE : View.GONE);
+        rootView.findViewById(R.id.permission_needed)
+                .setVisibility(show ? View.GONE : View.VISIBLE);
+    }
+
     @Override
     public void onDestroyView() {
         super.onDestroyView();
@@ -178,6 +225,9 @@
 
     @Override
     protected void setUpBottomSheetView(ViewGroup bottomSheet) {
+        if (USE_NEW_UI) {
+            return;
+        }
 
         initInfoPage();
         initSettingsPage();
@@ -242,6 +292,97 @@
         mViewPager.setCurrentItem(0);
     }
 
+    private void previewLiveWallpaper(ViewGroup container, ImageView thumbnailView) {
+        container.post(() -> {
+            // TODO(chriscsli): Add thumbnail preview for wallpaper binding failed case
+            LiveTileOverlay.INSTANCE.detach(thumbnailView.getOverlay());
+
+            setUpLiveWallpaperPreview(mWallpaper, thumbnailView,
+                    new ColorDrawable(getResources().getColor(
+                            R.color.secondary_color, getActivity().getTheme())));
+        });
+    }
+
+    private void setUpLiveWallpaperPreview(com.android.wallpaper.model.WallpaperInfo homeWallpaper,
+            ImageView previewView, Drawable thumbnail) {
+        Activity activity = getActivity();
+        if (activity == null) {
+            return;
+        }
+        if (mWallpaperConnection != null) {
+            mWallpaperConnection.disconnect();
+        }
+        if (thumbnail != null) {
+            thumbnail.setBounds(previewView.getLeft(), previewView.getTop(), previewView.getRight(),
+                    previewView.getBottom());
+        }
+        previewView.getLocationOnScreen(mLivePreviewLocation);
+        mPreviewGlobalRect.set(0, 0, previewView.getMeasuredWidth(),
+                previewView.getMeasuredHeight());
+        mPreviewLocalRect.set(mPreviewGlobalRect);
+        mPreviewGlobalRect.offset(mLivePreviewLocation[0], mLivePreviewLocation[1]);
+
+        mWallpaperConnection = new WallpaperConnection(
+                getWallpaperIntent(homeWallpaper.getWallpaperComponent()), activity,
+                new WallpaperConnection.WallpaperConnectionListener() {
+                    @Override
+                    public void onEngineShown() {
+                        mLoadingScrim.post(() -> mLoadingScrim.animate()
+                                .alpha(0f)
+                                .setDuration(220)
+                                .setStartDelay(300)
+                                .setInterpolator(AnimationUtils.loadInterpolator(getActivity(),
+                                        android.R.interpolator.fast_out_linear_in))
+                                .withEndAction(() -> {
+                                    if (mLoadingProgressBar != null) {
+                                        mLoadingProgressBar.hide();
+                                    }
+                                    mLoadingScrim.setVisibility(View.GONE);
+                                    mBottomActionBar.populateInfoPage(
+                                            mWallpaper.getAttributions(getContext()),
+                                            shouldShowMetadataInPreview());
+                                }));
+                        final Drawable placeholder = previewView.getDrawable() == null
+                                ? new ColorDrawable(getResources().getColor(R.color.secondary_color,
+                                activity.getTheme()))
+                                : previewView.getDrawable();
+                        LiveTileOverlay.INSTANCE.setForegroundDrawable(placeholder);
+                        LiveTileOverlay.INSTANCE.attach(previewView.getOverlay());
+                        previewView.animate()
+                                .setStartDelay(50)
+                                .setDuration(200)
+                                .setInterpolator(AnimationUtils.loadInterpolator(getContext(),
+                                        android.R.interpolator.fast_out_linear_in))
+                                .setUpdateListener(value -> placeholder.setAlpha(
+                                        (int) (255 * (1 - value.getAnimatedFraction()))))
+                                .withEndAction(() -> {
+                                    LiveTileOverlay.INSTANCE.setForegroundDrawable(null);
+                                }).start();
+                    }
+                }, mPreviewGlobalRect);
+
+        LiveTileOverlay.INSTANCE.update(new RectF(mPreviewLocalRect),
+                ((CardView) previewView.getParent()).getRadius());
+
+        mWallpaperConnection.setVisibility(true);
+        previewView.post(() -> {
+            if (!mWallpaperConnection.connect()) {
+                mWallpaperConnection = null;
+                LiveTileOverlay.INSTANCE.detach(previewView.getOverlay());
+            }
+        });
+    }
+
+    protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
+        if (USE_NEW_UI) {
+            mBottomActionBar = bottomActionBar;
+            mBottomActionBar.showActionsOnly(INFORMATION, CUSTOMIZE, APPLY);
+            mBottomActionBar.setActionClickListener(APPLY, unused ->
+                    this.onSetWallpaperClicked(null));
+            mBottomActionBar.show();
+        }
+    }
+
     private void logLiveWallpaperPageSelected(int position) {
         switch (position) {
             case 0:
@@ -325,7 +466,7 @@
 
     @Override
     protected int getLayoutResId() {
-        return R.layout.fragment_live_preview;
+        return USE_NEW_UI ? R.layout.fragment_live_preview_v2 : R.layout.fragment_live_preview;
     }
 
     @Override
diff --git a/src/com/android/wallpaper/picker/PreviewFragment.java b/src/com/android/wallpaper/picker/PreviewFragment.java
index 7c29e9d..9fda6f5 100755
--- a/src/com/android/wallpaper/picker/PreviewFragment.java
+++ b/src/com/android/wallpaper/picker/PreviewFragment.java
@@ -104,6 +104,8 @@
     public static final String ARG_PREVIEW_MODE = "preview_mode";
     public static final String ARG_TESTING_MODE_ENABLED = "testing_mode_enabled";
 
+    protected static final boolean USE_NEW_UI = ViewOnlyPreviewActivity.USE_NEW_UI;
+
     /**
      * Creates and returns new instance of {@link ImagePreviewFragment} with the provided wallpaper
      * set as an argument.
@@ -202,21 +204,28 @@
 
         // Set toolbar as the action bar.
         Toolbar toolbar = view.findViewById(R.id.toolbar);
+        if (USE_NEW_UI) {
+            TextView titleTextView = toolbar.findViewById(R.id.custom_toolbar_title);
+            if (titleTextView != null) {
+                titleTextView.setText(R.string.preview);
+            }
+        }
         AppCompatActivity activity = (AppCompatActivity) requireActivity();
         activity.setSupportActionBar(toolbar);
         activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
         activity.getSupportActionBar().setDisplayShowTitleEnabled(false);
-
         toolbar.getNavigationIcon().setTint(getAttrColor(activity, android.R.attr.colorPrimary));
         toolbar.getNavigationIcon().setAutoMirrored(true);
 
-        ViewCompat.setPaddingRelative(toolbar,
-        /* start */ getResources().getDimensionPixelSize(
-                        R.dimen.preview_toolbar_up_button_start_padding),
-        /* top */ 0,
-        /* end */ getResources().getDimensionPixelSize(
-                        R.dimen.preview_toolbar_set_wallpaper_button_end_padding),
-        /* bottom */ 0);
+        if (!USE_NEW_UI) {
+            ViewCompat.setPaddingRelative(toolbar,
+                    /* start */ getResources().getDimensionPixelSize(
+                            R.dimen.preview_toolbar_up_button_start_padding),
+                    /* top */ 0,
+                    /* end */ getResources().getDimensionPixelSize(
+                            R.dimen.preview_toolbar_set_wallpaper_button_end_padding),
+                    /* bottom */ 0);
+        }
 
         mLoadingProgressBar = view.findViewById(getLoadingIndicatorResId());
         mLoadingProgressBar.show();
@@ -239,18 +248,21 @@
                 : savedInstanceState.getInt(KEY_BOTTOM_SHEET_STATE, STATE_EXPANDED);
         setUpBottomSheetListeners();
 
-        view.setOnApplyWindowInsetsListener((v, windowInsets) -> {
-            toolbar.setPadding(toolbar.getPaddingLeft(),
-                    toolbar.getPaddingTop() + windowInsets.getSystemWindowInsetTop(),
-                    toolbar.getPaddingRight(), toolbar.getPaddingBottom());
-            mBottomSheet.setPadding(mBottomSheet.getPaddingLeft(),
-                    mBottomSheet.getPaddingTop(), mBottomSheet.getPaddingRight(),
-                    mBottomSheet.getPaddingBottom() + windowInsets.getSystemWindowInsetBottom());
-            WindowInsets.Builder builder = new WindowInsets.Builder(windowInsets);
-            builder.setSystemWindowInsets(Insets.of(windowInsets.getSystemWindowInsetLeft(),
-                    0, windowInsets.getStableInsetRight(), 0));
-            return builder.build();
-        });
+        if (!USE_NEW_UI) {
+            view.setOnApplyWindowInsetsListener((v, windowInsets) -> {
+                toolbar.setPadding(toolbar.getPaddingLeft(),
+                        toolbar.getPaddingTop() + windowInsets.getSystemWindowInsetTop(),
+                        toolbar.getPaddingRight(), toolbar.getPaddingBottom());
+                mBottomSheet.setPadding(mBottomSheet.getPaddingLeft(),
+                        mBottomSheet.getPaddingTop(), mBottomSheet.getPaddingRight(),
+                        mBottomSheet.getPaddingBottom()
+                                + windowInsets.getSystemWindowInsetBottom());
+                WindowInsets.Builder builder = new WindowInsets.Builder(windowInsets);
+                builder.setSystemWindowInsets(Insets.of(windowInsets.getSystemWindowInsetLeft(),
+                        0, windowInsets.getStableInsetRight(), 0));
+                return builder.build();
+            });
+        }
 
         return view;
     }
diff --git a/src/com/android/wallpaper/picker/ViewOnlyPreviewActivity.java b/src/com/android/wallpaper/picker/ViewOnlyPreviewActivity.java
index f10cc51..a5d4486 100755
--- a/src/com/android/wallpaper/picker/ViewOnlyPreviewActivity.java
+++ b/src/com/android/wallpaper/picker/ViewOnlyPreviewActivity.java
@@ -19,19 +19,20 @@
 import android.content.Intent;
 import android.os.Bundle;
 
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+
 import com.android.wallpaper.R;
 import com.android.wallpaper.model.InlinePreviewIntentFactory;
 import com.android.wallpaper.model.WallpaperInfo;
 import com.android.wallpaper.module.InjectorProvider;
 
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
-
 /**
  * Activity that displays a view-only preview of a specific wallpaper.
  */
 public class ViewOnlyPreviewActivity extends BasePreviewActivity {
 
+    public static boolean USE_NEW_UI = true;
     /**
      * Returns a new Intent with the provided WallpaperInfo instance put as an extra.
      */
diff --git a/src/com/android/wallpaper/widget/BottomActionBar.java b/src/com/android/wallpaper/widget/BottomActionBar.java
index b5bbdd5..bbe9c7d 100644
--- a/src/com/android/wallpaper/widget/BottomActionBar.java
+++ b/src/com/android/wallpaper/widget/BottomActionBar.java
@@ -62,6 +62,7 @@
     }
 
     private final Map<BottomAction, View> mActionMap = new EnumMap<>(BottomAction.class);
+    private final ViewGroup mBottomSheet;
     private final BottomSheetBehavior<ViewGroup> mBottomSheetBehavior;
     private final TextView mAttributionTitle;
     private final TextView mAttributionSubtitle1;
@@ -84,20 +85,20 @@
         mActionMap.put(BottomAction.PROGRESS, findViewById(R.id.action_progress));
         mActionMap.put(BottomAction.APPLY, findViewById(R.id.action_apply));
 
-        ViewGroup bottomSheet = findViewById(R.id.action_bottom_sheet);
-        mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
+        mBottomSheet = findViewById(R.id.action_bottom_sheet);
+        mBottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);
         mBottomSheetBehavior.setState(STATE_COLLAPSED);
 
         // Workaround as we don't have access to bottomDialogCornerRadius, mBottomSheet radii are
         // set to dialogCornerRadius by default.
-        GradientDrawable bottomSheetBackground = (GradientDrawable) bottomSheet.getBackground();
+        GradientDrawable bottomSheetBackground = (GradientDrawable) mBottomSheet.getBackground();
         float[] radii = bottomSheetBackground.getCornerRadii();
         for (int i = 0; i < radii.length; i++) {
             radii[i]*=2f;
         }
         bottomSheetBackground = ((GradientDrawable)bottomSheetBackground.mutate());
         bottomSheetBackground.setCornerRadii(radii);
-        bottomSheet.setBackground(bottomSheetBackground);
+        mBottomSheet.setBackground(bottomSheetBackground);
 
         ImageView informationIcon = findViewById(R.id.action_information);
         mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetCallback() {
@@ -156,12 +157,15 @@
 
         // Ensure the ClickListener can work normally if has info been populated, since it could be
         // removed by #clearActionClickListeners.
-        setActionClickListener(BottomAction.INFORMATION, unused ->
+        setActionClickListener(BottomAction.INFORMATION, unused -> {
+            mBottomSheet.setVisibility(mBottomSheetBehavior.getState() == STATE_COLLAPSED
+                    ? VISIBLE
+                    : GONE);
             mBottomSheetBehavior.setState(mBottomSheetBehavior.getState() == STATE_COLLAPSED
-                ? STATE_EXPANDED
-                : STATE_COLLAPSED
-            )
-        );
+                    ? STATE_EXPANDED
+                    : STATE_COLLAPSED
+            );
+        });
 
         if (attributions.size() > 0 && attributions.get(0) != null) {
             mAttributionTitle.setText(attributions.get(0));
diff --git a/src/com/android/wallpaper/widget/WallpaperPreviewCard.java b/src/com/android/wallpaper/widget/WallpaperPreviewCard.java
new file mode 100644
index 0000000..e57d946
--- /dev/null
+++ b/src/com/android/wallpaper/widget/WallpaperPreviewCard.java
@@ -0,0 +1,66 @@
+/*
+ * 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.graphics.Point;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+
+import com.android.wallpaper.R;
+import com.android.wallpaper.util.ScreenSizeCalculator;
+
+/**
+ * A Widget consists of a CardView to show proper preview based on screen aspect ratio.
+ */
+public class WallpaperPreviewCard extends LinearLayout {
+
+    private float mScreenAspectRatio;
+
+    public WallpaperPreviewCard(Context context) {
+        this(context, null);
+    }
+
+    public WallpaperPreviewCard(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WallpaperPreviewCard(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        LayoutInflater.from(context).inflate(R.layout.wallpaper_preview_card_layout, this);
+        WindowManager windowManager = getContext().getSystemService(WindowManager.class);
+        Point screenSize = ScreenSizeCalculator.getInstance()
+                .getScreenSize(windowManager.getDefaultDisplay());
+        mScreenAspectRatio = (float) screenSize.y / screenSize.x;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int measuredViewHeight = this.getMeasuredHeight();
+        int measuredViewWidth = this.getMeasuredWidth();
+        int absoluteViewWidth = (int) ((measuredViewHeight - this.getPaddingBottom()
+                - this.getPaddingTop()) / mScreenAspectRatio);
+        int horizontalPadding = (measuredViewWidth - absoluteViewWidth) / 2;
+        this.setPaddingRelative(
+                horizontalPadding,
+                this.getPaddingTop(),
+                horizontalPadding,
+                this.getPaddingBottom());
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+}