Snap for 6198741 from 54e964b29ce87555c08383732823c13e5c4938dd to sdk-release

Change-Id: I3d05330d9c19a150419a872f2dd2e175ee18602d
diff --git a/res/layout/fragment_category_picker.xml b/res/layout/fragment_category_picker.xml
index 51bf8d7..3c74180 100755
--- a/res/layout/fragment_category_picker.xml
+++ b/res/layout/fragment_category_picker.xml
@@ -26,6 +26,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:clipToPadding="false"
+        android:fitsSystemWindows="true"
         android:paddingLeft="@dimen/grid_padding"
         android:paddingTop="@dimen/grid_padding"
         android:scrollbarSize="@dimen/grid_padding"
diff --git a/res/layout/preview_page_info.xml b/res/layout/preview_page_info.xml
index 587e120..9299426 100644
--- a/res/layout/preview_page_info.xml
+++ b/res/layout/preview_page_info.xml
@@ -49,7 +49,7 @@
         android:layout_marginTop="@dimen/preview_attribution_pane_author_top_margin"
         android:forceHasOverlappingRendering="false"
         android:gravity="center"
-        android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead"
+        android:textAppearance="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle"
         android:textColor="@color/material_white_100"
         android:visibility="gone"/>
 
@@ -60,7 +60,8 @@
         android:minHeight="@dimen/preview_attribution_pane_description_height"
         android:forceHasOverlappingRendering="false"
         android:gravity="center_horizontal"
-        android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Caption"
+        android:textAppearance="@android:style/TextAppearance.DeviceDefault.Small"
+        android:textSize="12dp"
         android:textColor="@color/material_white_100"
         android:visibility="gone"/>
 
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index a95e589..d96971c 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -28,7 +28,7 @@
     <string name="load_wallpaper_error_message" msgid="7913278480467707374">"No se puede cargar el fondo de pantalla. La imagen está dañada o no está disponible."</string>
     <string name="static_wallpaper_presentation_mode_message" msgid="417940227049360906">"Establecido actualmente"</string>
     <string name="rotating_wallpaper_presentation_mode_message" msgid="3361676041605733288">"Fondo de pantalla diario"</string>
-    <string name="wallpaper_destination_both" msgid="1124197176741944063">"Pantalla principal y bloqueada"</string>
+    <string name="wallpaper_destination_both" msgid="1124197176741944063">"Pantalla principal y de bloqueo"</string>
     <string name="home_screen_message" msgid="106444102822522813">"Pantalla principal"</string>
     <string name="lock_screen_message" msgid="1534506081955058013">"Pantalla bloqueada"</string>
     <string name="home_and_lock_short_label" msgid="2937922943541927983">"Pantalla principal y bloqueada"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 269f05d..453ecb0 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -37,9 +37,9 @@
     <string name="set_wallpaper_lock_screen_destination" msgid="6224685559375417945">"लॉक स्‍क्रीन"</string>
     <string name="set_wallpaper_both_destination" msgid="6967226064958263939">"होम स्क्रीन और लॉक स्क्रीन"</string>
     <string name="no_backup_image_wallpaper_label" msgid="6316627676107284851">"तय समय के बाद बदलने वाला इमेज वॉलपेपर"</string>
-    <string name="permission_needed_explanation" msgid="139166837541426823">"यहां पर मौजूदा वॉलपेपर दिखाने के लिए, <xliff:g id="APP_NAME">%1$s</xliff:g> को आपके डिवाइस की मेमोरी का एक्सेस चाहिए."</string>
-    <string name="permission_needed_explanation_go_to_settings" msgid="3923551582092599609">"यहां पर मौजूदा वॉलपेपर दिखाने के लिए, वॉलपेपर को आपके डिवाइस की मेमोरी का एक्सेस चाहिए. \n\nवॉलपेपर ऐप्लिकेशन की जानकारी के मंज़ूरी वाले विकल्प पर जाकर आप यह सेटिंग बदल सकते हैं."</string>
-    <string name="permission_needed_allow_access_button_label" msgid="1943133660612924306">"एक्सेस दें"</string>
+    <string name="permission_needed_explanation" msgid="139166837541426823">"यहां पर मौजूदा वॉलपेपर दिखाने के लिए, <xliff:g id="APP_NAME">%1$s</xliff:g> को आपके डिवाइस की मेमोरी का ऐक्सेस चाहिए."</string>
+    <string name="permission_needed_explanation_go_to_settings" msgid="3923551582092599609">"यहां पर मौजूदा वॉलपेपर दिखाने के लिए, वॉलपेपर को आपके डिवाइस की मेमोरी का ऐक्सेस चाहिए. \n\nवॉलपेपर ऐप्लिकेशन की जानकारी के मंज़ूरी वाले विकल्प पर जाकर आप यह सेटिंग बदल सकते हैं."</string>
+    <string name="permission_needed_allow_access_button_label" msgid="1943133660612924306">"ऐक्सेस दें"</string>
     <string name="no_backup_image_wallpaper_description" msgid="8303268619408738057">"तय समय के बाद बदलने वाले वॉलपेपर के लिए लाइव वॉलपेपर सेवा"</string>
     <string name="daily_refresh_tile_title" msgid="3270456074558525091">"रोज़ वॉलपेपर बदलने की सुविधा"</string>
     <string name="daily_refresh_tile_subtitle" msgid="3976682014885446443">"चालू करने के लिए टैप करें"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 565f9c4..afe8069 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -42,8 +42,8 @@
     <string name="permission_needed_allow_access_button_label" msgid="1943133660612924306">"Izinkan akses"</string>
     <string name="no_backup_image_wallpaper_description" msgid="8303268619408738057">"Layanan wallpaper animasi untuk memutar wallpaper"</string>
     <string name="daily_refresh_tile_title" msgid="3270456074558525091">"Wallpaper harian"</string>
-    <string name="daily_refresh_tile_subtitle" msgid="3976682014885446443">"Tap untuk mengaktifkan"</string>
-    <string name="start_rotation_dialog_body_live_wallpaper_needed" msgid="5132580257563846082">"Wallpaper akan otomatis berubah setiap hari. Untuk menyelesaikan penyiapan, tap &lt;strong&gt;Setel wallpaper&lt;/strong&gt; di layar selanjutnya."</string>
+    <string name="daily_refresh_tile_subtitle" msgid="3976682014885446443">"Ketuk untuk mengaktifkan"</string>
+    <string name="start_rotation_dialog_body_live_wallpaper_needed" msgid="5132580257563846082">"Wallpaper akan otomatis berubah setiap hari. Untuk menyelesaikan penyiapan, ketuk &lt;strong&gt;Setel wallpaper&lt;/strong&gt; di layar selanjutnya."</string>
     <string name="start_rotation_dialog_wifi_only_option_message" msgid="3126269859713666225">"Download wallpaper mendatang hanya melalui Wi-Fi"</string>
     <string name="start_rotation_dialog_continue" msgid="276678987852274872">"Lanjutkan"</string>
     <string name="start_rotation_progress_message" msgid="7872623873682262083">"Mendownload wallpaper pertama…"</string>
diff --git a/res/values-notnight-v27/styles.xml b/res/values-notnight-v27/styles.xml
index 683d09d..e090c4c 100755
--- a/res/values-notnight-v27/styles.xml
+++ b/res/values-notnight-v27/styles.xml
@@ -20,10 +20,8 @@
         <item name="colorPrimary">?android:colorPrimary</item>
         <item name="colorControlActivated">?attr/colorPrimary</item>
         <item name="colorControlNormal">?android:attr/colorPrimary</item>
-        <item name="android:statusBarColor">?attr/colorPrimary</item>
-        <item name="android:windowLightStatusBar">true</item>
 
-        <item name="android:navigationBarColor">?android:colorPrimaryDark</item>
+        <item name="android:navigationBarColor">@android:color/transparent</item>
         <item name="android:navigationBarDividerColor">@android:color/transparent</item>
         <item name="android:windowLightNavigationBar">true</item>
 
@@ -39,8 +37,6 @@
 
         <item name="android:windowActionBar">false</item>
         <item name="android:windowNoTitle">true</item>
-        <item name="android:fitsSystemWindows">false</item>
-        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
     </style>
 
     <!-- Dialog themes -->
diff --git a/res/values-v29/styles.xml b/res/values-v29/styles.xml
new file mode 100644
index 0000000..e7f27d4
--- /dev/null
+++ b/res/values-v29/styles.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<resources>
+    <style name="PreviewCheckboxDeviceTheme" parent="@android:style/ThemeOverlay.DeviceDefault.Accent.DayNight">
+        <item name="android:colorControlActivated">?android:attr/colorAccent</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 45b6d02..6259281 100755
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<resources xmlns:tools="http://schemas.android.com/tools" tools:targetApi="23">
+<resources>
 
     <!-- Main themes -->
     <style name="WallpaperTheme" parent="@android:style/Theme.DeviceDefault.Settings">
@@ -87,9 +87,7 @@
     </style>
 
     <!-- Individual components / Widgets -->
-    <style name="HeaderTextAppearance" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
-        <item name="android:textFontWeight">400</item>
-    </style>
+    <style name="HeaderTextAppearance" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"/>
 
     <style name="ButtonStyle" parent="@android:style/Widget.DeviceDefault.Button.Colored">
         <item name="android:padding">16dp</item>
@@ -104,9 +102,7 @@
 
     <style name="ActionBarCheckboxStyle" parent="@android:style/Widget.DeviceDefault.CompoundButton.CheckBox"/>
 
-    <style name="PreviewCheckboxDeviceTheme" parent="@android:style/Theme.DeviceDefault">
-        <item name="android:colorControlActivated">@*android:color/accent_device_default_light</item>
-    </style>
+    <style name="PreviewCheckboxDeviceTheme" parent="@android:style/Theme.DeviceDefault"/>
 
     <style name="select_wallpaper_header">
         <item name="android:textColor">@color/translucent_black_60_alpha</item>
diff --git a/src/com/android/wallpaper/asset/Asset.java b/src/com/android/wallpaper/asset/Asset.java
index c1d2252..498fad7 100755
--- a/src/com/android/wallpaper/asset/Asset.java
+++ b/src/com/android/wallpaper/asset/Asset.java
@@ -27,6 +27,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.TransitionDrawable;
 import android.os.AsyncTask;
+import android.view.View;
 import android.widget.ImageView;
 
 import androidx.annotation.Nullable;
@@ -44,7 +45,7 @@
      */
     protected static Drawable getPlaceholderDrawable(
             Context context, ImageView imageView, int placeholderColor) {
-        Point imageViewDimensions = getImageViewDimensions(imageView);
+        Point imageViewDimensions = getViewDimensions(imageView);
         Bitmap placeholderBitmap =
                 Bitmap.createBitmap(imageViewDimensions.x, imageViewDimensions.y, Config.ARGB_8888);
         placeholderBitmap.eraseColor(placeholderColor);
@@ -55,13 +56,10 @@
      * Returns the visible height and width in pixels of the provided ImageView, or if it hasn't been
      * laid out yet, then gets the absolute value of the layout params.
      */
-    private static Point getImageViewDimensions(ImageView imageView) {
-        int width = imageView.getWidth() > 0
-                ? imageView.getWidth()
-                : Math.abs(imageView.getLayoutParams().width);
-        int height = imageView.getHeight() > 0
-                ? imageView.getHeight()
-                : Math.abs(imageView.getLayoutParams().height);
+    private static Point getViewDimensions(View view) {
+        int width = view.getWidth() > 0 ? view.getWidth() : Math.abs(view.getLayoutParams().width);
+        int height = view.getHeight() > 0 ? view.getHeight()
+                : Math.abs(view.getLayoutParams().height);
 
         return new Point(width, height);
     }
@@ -191,7 +189,7 @@
             final int transitionDurationMillis,
             @Nullable final DrawableLoadedListener drawableLoadedListener,
             int placeholderColor) {
-        Point imageViewDimensions = getImageViewDimensions(imageView);
+        Point imageViewDimensions = getViewDimensions(imageView);
 
         // Transition from a placeholder ColorDrawable to the decoded bitmap when the ImageView in
         // question is empty.
@@ -275,7 +273,7 @@
      * Custom AsyncTask which returns a copy of the given bitmap which is center cropped and scaled to
      * fit in the given ImageView.
      */
-    protected static class CenterCropBitmapTask extends AsyncTask<Void, Void, Bitmap> {
+    public static class CenterCropBitmapTask extends AsyncTask<Void, Void, Bitmap> {
 
         private Bitmap mBitmap;
         private BitmapReceiver mBitmapReceiver;
@@ -283,12 +281,12 @@
         private int mImageViewWidth;
         private int mImageViewHeight;
 
-        public CenterCropBitmapTask(Bitmap bitmap, ImageView imageView,
+        public CenterCropBitmapTask(Bitmap bitmap, View view,
                                     BitmapReceiver bitmapReceiver) {
             mBitmap = bitmap;
             mBitmapReceiver = bitmapReceiver;
 
-            Point imageViewDimensions = getImageViewDimensions(imageView);
+            Point imageViewDimensions = getViewDimensions(view);
 
             mImageViewWidth = imageViewDimensions.x;
             mImageViewHeight = imageViewDimensions.y;
@@ -307,7 +305,8 @@
                     (float) bitmapHeight / measuredHeight);
 
             Bitmap scaledBitmap = Bitmap.createScaledBitmap(
-                    mBitmap, Math.round(bitmapWidth / scale), Math.round(bitmapHeight / scale), true);
+                    mBitmap, Math.round(bitmapWidth / scale), Math.round(bitmapHeight / scale),
+                    true);
 
             int horizontalGutterPx = Math.max(0, (scaledBitmap.getWidth() - measuredWidth) / 2);
             int verticalGutterPx = Math.max(0, (scaledBitmap.getHeight() - measuredHeight) / 2);
diff --git a/src/com/android/wallpaper/asset/LiveWallpaperThumbAsset.java b/src/com/android/wallpaper/asset/LiveWallpaperThumbAsset.java
index 0214030..5df68c5 100755
--- a/src/com/android/wallpaper/asset/LiveWallpaperThumbAsset.java
+++ b/src/com/android/wallpaper/asset/LiveWallpaperThumbAsset.java
@@ -17,6 +17,7 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
@@ -35,9 +36,9 @@
 /**
  * Asset wrapping a drawable for a live wallpaper thumbnail.
  */
-public final class LiveWallpaperThumbAsset extends Asset {
-    private final Context mContext;
-    private final android.app.WallpaperInfo mInfo;
+public class LiveWallpaperThumbAsset extends Asset {
+    protected final Context mContext;
+    protected final android.app.WallpaperInfo mInfo;
 
     public LiveWallpaperThumbAsset(Context context, android.app.WallpaperInfo info) {
         mContext = context.getApplicationContext();
@@ -48,7 +49,7 @@
     public void decodeBitmap(int targetWidth, int targetHeight,
                              BitmapReceiver receiver) {
         // No scaling is needed, as the thumbnail is already a thumbnail.
-        LoadThumbnailTask task = new LoadThumbnailTask(mInfo, receiver);
+        LoadThumbnailTask task = new LoadThumbnailTask(mContext, mInfo, receiver);
         task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 
@@ -91,7 +92,7 @@
      * Returns the thumbnail drawable for the live wallpaper synchronously. Should not be called on
      * the main UI thread.
      */
-    Drawable getThumbnailDrawable() {
+    protected Drawable getThumbnailDrawable() {
         return mInfo.loadThumbnail(mContext.getPackageManager());
     }
 
@@ -147,18 +148,21 @@
      * AsyncTask subclass which loads the live wallpaper's thumbnail bitmap off the main UI thread.
      * Resolves with null if live wallpaper thumbnail is not a bitmap.
      */
-    private class LoadThumbnailTask extends AsyncTask<Void, Void, Bitmap> {
+    private static class LoadThumbnailTask extends AsyncTask<Void, Void, Bitmap> {
+        private final PackageManager mPackageManager;
         private android.app.WallpaperInfo mInfo;
         private BitmapReceiver mReceiver;
 
-        public LoadThumbnailTask(android.app.WallpaperInfo info, BitmapReceiver receiver) {
+        public LoadThumbnailTask(Context context, android.app.WallpaperInfo info,
+                BitmapReceiver receiver) {
             mInfo = info;
             mReceiver = receiver;
+            mPackageManager = context.getPackageManager();
         }
 
         @Override
         protected Bitmap doInBackground(Void... unused) {
-            Drawable thumb = mInfo.loadThumbnail(mContext.getPackageManager());
+            Drawable thumb = mInfo.loadThumbnail(mPackageManager);
 
             // Live wallpaper components may or may not specify a thumbnail drawable.
             if (thumb != null && thumb instanceof BitmapDrawable) {
diff --git a/src/com/android/wallpaper/asset/StreamableAsset.java b/src/com/android/wallpaper/asset/StreamableAsset.java
index 118581d..17f2522 100755
--- a/src/com/android/wallpaper/asset/StreamableAsset.java
+++ b/src/com/android/wallpaper/asset/StreamableAsset.java
@@ -260,12 +260,6 @@
                 mTargetWidth = tempHeight;
             }
 
-            InputStream inputStream = openInputStream();
-            // Input stream may be null if there was an error opening it.
-            if (inputStream == null) {
-                return null;
-            }
-
             BitmapFactory.Options options = new BitmapFactory.Options();
 
             Point rawDimensions = calculateRawDimensions();
@@ -277,6 +271,7 @@
                     rawDimensions.x, rawDimensions.y, mTargetWidth, mTargetHeight);
             options.inPreferredConfig = Config.HARDWARE;
 
+            InputStream inputStream = openInputStream();
             Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
             closeInputStream(
                     inputStream, "Error closing the input stream used to decode the full bitmap");
diff --git a/src/com/android/wallpaper/model/EmptyCategory.java b/src/com/android/wallpaper/model/EmptyCategory.java
new file mode 100644
index 0000000..d37da90
--- /dev/null
+++ b/src/com/android/wallpaper/model/EmptyCategory.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+package com.android.wallpaper.model;
+
+import android.app.Activity;
+import android.content.Context;
+
+import com.android.wallpaper.asset.Asset;
+
+/**
+ * A placeholder {@link Category} with only id and title (and no content).
+ * Typically used to display partially loaded categories.
+ */
+public class EmptyCategory extends Category {
+
+    /**
+     * Constructs an EmptyCategory object.
+     *
+     * @see Category#Category(String, String, int)
+     */
+    public EmptyCategory(String title, String collectionId, int priority) {
+        super(title, collectionId, priority);
+    }
+
+    @Override
+    public void show(Activity srcActivity, PickerIntentFactory factory, int requestCode) {
+
+    }
+
+    @Override
+    public Asset getThumbnail(Context context) {
+        return null;
+    }
+}
diff --git a/src/com/android/wallpaper/model/LiveWallpaperInfo.java b/src/com/android/wallpaper/model/LiveWallpaperInfo.java
index 5fc3cea..a408aed 100755
--- a/src/com/android/wallpaper/model/LiveWallpaperInfo.java
+++ b/src/com/android/wallpaper/model/LiveWallpaperInfo.java
@@ -28,10 +28,14 @@
 import android.service.wallpaper.WallpaperService;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.wallpaper.R;
 import com.android.wallpaper.asset.Asset;
 import com.android.wallpaper.asset.LiveWallpaperThumbAsset;
 import com.android.wallpaper.compat.BuildCompat;
+import com.android.wallpaper.module.InjectorProvider;
+import com.android.wallpaper.module.LiveWallpaperInfoFactory;
 import com.android.wallpaper.util.ActivityUtils;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -45,8 +49,6 @@
 import java.util.Iterator;
 import java.util.List;
 
-import androidx.annotation.Nullable;
-
 /**
  * Represents a live wallpaper from the system.
  */
@@ -64,8 +66,8 @@
                 }
             };
     private static final String TAG = "LiveWallpaperInfo";
-    private android.app.WallpaperInfo mInfo;
-    private LiveWallpaperThumbAsset mThumbAsset;
+    protected android.app.WallpaperInfo mInfo;
+    protected LiveWallpaperThumbAsset mThumbAsset;
     private boolean mVisibleTitle;
 
     /**
@@ -100,7 +102,8 @@
                                              @Nullable List<String> excludedPackageNames) {
         List<ResolveInfo> resolveInfos = getAllOnDevice(context);
         List<WallpaperInfo> wallpaperInfos = new ArrayList<>();
-
+        LiveWallpaperInfoFactory factory =
+                InjectorProvider.getInjector().getLiveWallpaperInfoFactory(context);
         for (int i = 0; i < resolveInfos.size(); i++) {
             ResolveInfo resolveInfo = resolveInfos.get(i);
             android.app.WallpaperInfo wallpaperInfo;
@@ -119,7 +122,7 @@
                 continue;
             }
 
-            wallpaperInfos.add(new LiveWallpaperInfo(wallpaperInfo));
+            wallpaperInfos.add(factory.getLiveWallpaperInfo(wallpaperInfo));
         }
 
         return wallpaperInfos;
@@ -139,6 +142,8 @@
             resolveInfos = getAllOnDevice(context);
         }
         List<WallpaperInfo> wallpaperInfos = new ArrayList<>();
+        LiveWallpaperInfoFactory factory =
+                InjectorProvider.getInjector().getLiveWallpaperInfoFactory(context);
 
         for (int i = 0; i < resolveInfos.size(); i++) {
             ResolveInfo resolveInfo = resolveInfos.get(i);
@@ -162,7 +167,7 @@
                 continue;
             }
 
-            wallpaperInfos.add(new LiveWallpaperInfo(wallpaperInfo, shouldShowTitle));
+            wallpaperInfos.add(factory.getLiveWallpaperInfo(wallpaperInfo, shouldShowTitle));
         }
 
         return wallpaperInfos;
diff --git a/src/com/android/wallpaper/module/BaseWallpaperInjector.java b/src/com/android/wallpaper/module/BaseWallpaperInjector.java
index 15ed84b..f8191fc 100755
--- a/src/com/android/wallpaper/module/BaseWallpaperInjector.java
+++ b/src/com/android/wallpaper/module/BaseWallpaperInjector.java
@@ -43,6 +43,7 @@
     private FormFactorChecker mFormFactorChecker;
     private PackageStatusNotifier mPackageStatusNotifier;
     private LiveWallpaperInfoFactory mLiveWallpaperInfoFactory;
+    private DrawableLayerResolver mDrawableLayerResolver;
 
     @Override
     public synchronized BitmapCropper getBitmapCropper() {
@@ -187,4 +188,12 @@
         }
         return mLiveWallpaperInfoFactory;
     }
+
+    @Override
+    public DrawableLayerResolver getDrawableLayerResolver() {
+        if (mDrawableLayerResolver == null) {
+            mDrawableLayerResolver = new DefaultDrawableLayerResolver();
+        }
+        return mDrawableLayerResolver;
+    }
 }
diff --git a/src/com/android/wallpaper/module/DefaultDrawableLayerResolver.java b/src/com/android/wallpaper/module/DefaultDrawableLayerResolver.java
new file mode 100644
index 0000000..4e52ff8
--- /dev/null
+++ b/src/com/android/wallpaper/module/DefaultDrawableLayerResolver.java
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+package com.android.wallpaper.module;
+
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+
+/**
+ * Default implementation of {@link DrawableLayerResolver}
+ */
+public class DefaultDrawableLayerResolver implements DrawableLayerResolver {
+
+    /**
+     * Picks and returns the top most layer of the given {@link LayerDrawable}
+     */
+    @Override
+    public Drawable resolveLayer(LayerDrawable layerDrawable) {
+        return layerDrawable.getDrawable(layerDrawable.getNumberOfLayers() - 1);
+    }
+}
diff --git a/src/com/android/wallpaper/module/DefaultLiveWallpaperInfoFactory.java b/src/com/android/wallpaper/module/DefaultLiveWallpaperInfoFactory.java
index 4a06ce5..774ad89 100644
--- a/src/com/android/wallpaper/module/DefaultLiveWallpaperInfoFactory.java
+++ b/src/com/android/wallpaper/module/DefaultLiveWallpaperInfoFactory.java
@@ -1,3 +1,18 @@
+/*
+ * 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.
+ */
 package com.android.wallpaper.module;
 
 import com.android.wallpaper.model.LiveWallpaperInfo;
@@ -13,4 +28,10 @@
     public WallpaperInfo getLiveWallpaperInfo(android.app.WallpaperInfo info) {
         return new LiveWallpaperInfo(info);
     }
+
+    @Override
+    public WallpaperInfo getLiveWallpaperInfo(android.app.WallpaperInfo info,
+            boolean shouldShowTitle) {
+        return new LiveWallpaperInfo(info, shouldShowTitle);
+    }
 }
diff --git a/src/com/android/wallpaper/module/DrawableLayerResolver.java b/src/com/android/wallpaper/module/DrawableLayerResolver.java
new file mode 100644
index 0000000..5c61433
--- /dev/null
+++ b/src/com/android/wallpaper/module/DrawableLayerResolver.java
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+package com.android.wallpaper.module;
+
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+
+/**
+ * Interface for a class that can be used to arbitrarily select a Layer from a LayerDrawable
+ */
+public interface DrawableLayerResolver {
+
+    /**
+     * Picks a layer from a given {@link LayerDrawable}
+     */
+    Drawable resolveLayer(LayerDrawable layerDrawable);
+}
diff --git a/src/com/android/wallpaper/module/Injector.java b/src/com/android/wallpaper/module/Injector.java
index 6402496..3afaeb4 100755
--- a/src/com/android/wallpaper/module/Injector.java
+++ b/src/com/android/wallpaper/module/Injector.java
@@ -17,6 +17,8 @@
 
 import android.content.Context;
 
+import androidx.fragment.app.Fragment;
+
 import com.android.wallpaper.compat.WallpaperManagerCompat;
 import com.android.wallpaper.model.CategoryProvider;
 import com.android.wallpaper.model.WallpaperInfo;
@@ -25,8 +27,6 @@
 import com.android.wallpaper.picker.PreviewFragment.PreviewMode;
 import com.android.wallpaper.picker.individual.IndividualPickerFragment;
 
-import androidx.fragment.app.Fragment;
-
 /**
  * Interface for a provider of "injected dependencies." (NOTE: The term "injector" is somewhat of a
  * misnomer; this is more aptly a service registry as part of a service locator design pattern.)
@@ -83,4 +83,6 @@
     IndividualPickerFragment getIndividualPickerFragment(String collectionId);
 
     LiveWallpaperInfoFactory getLiveWallpaperInfoFactory(Context context);
+
+    DrawableLayerResolver getDrawableLayerResolver();
 }
diff --git a/src/com/android/wallpaper/module/LiveWallpaperInfoFactory.java b/src/com/android/wallpaper/module/LiveWallpaperInfoFactory.java
index 0c9bfee..838bbb6 100644
--- a/src/com/android/wallpaper/module/LiveWallpaperInfoFactory.java
+++ b/src/com/android/wallpaper/module/LiveWallpaperInfoFactory.java
@@ -1,6 +1,20 @@
+/*
+ * 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.
+ */
 package com.android.wallpaper.module;
 
-import com.android.wallpaper.model.LiveWallpaperInfo;
 import com.android.wallpaper.model.WallpaperInfo;
 
 /**
@@ -8,5 +22,7 @@
  */
 public interface LiveWallpaperInfoFactory {
 
-    public WallpaperInfo getLiveWallpaperInfo(android.app.WallpaperInfo info);
+    WallpaperInfo getLiveWallpaperInfo(android.app.WallpaperInfo info);
+
+    WallpaperInfo getLiveWallpaperInfo(android.app.WallpaperInfo info, boolean shouldShowTitle);
 }
diff --git a/src/com/android/wallpaper/picker/CategoryFragment.java b/src/com/android/wallpaper/picker/CategoryFragment.java
index 0e3c36d..f460a39 100755
--- a/src/com/android/wallpaper/picker/CategoryFragment.java
+++ b/src/com/android/wallpaper/picker/CategoryFragment.java
@@ -220,6 +220,7 @@
         }
         // Not add existing category to category list
         if (mCategories.indexOf(category) >= 0) {
+            updateCategory(category);
             return;
         }
 
@@ -341,7 +342,8 @@
     }
 
     private int getNumColumns() {
-        return TileSizeCalculator.getNumCategoryColumns(getActivity());
+        Activity activity = getActivity();
+        return activity == null ? 0 : TileSizeCalculator.getNumCategoryColumns(activity);
     }
 
     /**
diff --git a/src/com/android/wallpaper/picker/TopLevelPickerActivity.java b/src/com/android/wallpaper/picker/TopLevelPickerActivity.java
index ac6a13b..87358ec 100755
--- a/src/com/android/wallpaper/picker/TopLevelPickerActivity.java
+++ b/src/com/android/wallpaper/picker/TopLevelPickerActivity.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Color;
+import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.drawable.Drawable;
@@ -32,6 +33,7 @@
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.WindowInsets;
 import android.widget.Button;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -47,6 +49,7 @@
 
 import com.android.wallpaper.R;
 import com.android.wallpaper.asset.Asset;
+import com.android.wallpaper.compat.BuildCompat;
 import com.android.wallpaper.compat.ButtonDrawableSetterCompat;
 import com.android.wallpaper.config.Flags;
 import com.android.wallpaper.model.Category;
@@ -241,6 +244,29 @@
 
     private void initializeMobile(boolean shouldForceRefresh) {
         setContentView(R.layout.activity_single_fragment);
+        getWindow().getDecorView().setSystemUiVisibility(
+                getWindow().getDecorView().getSystemUiVisibility()
+                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+        findViewById(R.id.fragment_container)
+                .setOnApplyWindowInsetsListener((view, windowInsets) -> {
+            view.setPadding(view.getPaddingLeft(), windowInsets.getSystemWindowInsetTop(),
+                    view.getPaddingRight(), view.getBottom());
+            // Consume only the top inset (status bar), to let other content in the Activity consume
+            // the nav bar (ie, by using "fitSystemWindows")
+            if (BuildCompat.isAtLeastQ()) {
+                WindowInsets.Builder builder = new WindowInsets.Builder(windowInsets);
+                builder.setSystemWindowInsets(Insets.of(windowInsets.getSystemWindowInsetLeft(),
+                        0, windowInsets.getStableInsetRight(),
+                        windowInsets.getSystemWindowInsetBottom()));
+                return builder.build();
+            } else {
+                return windowInsets.replaceSystemWindowInsets(
+                        windowInsets.getSystemWindowInsetLeft(),
+                        0, windowInsets.getStableInsetRight(),
+                        windowInsets.getSystemWindowInsetBottom());
+            }
+        });
 
         // Set toolbar as the action bar.
         Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
diff --git a/src/com/android/wallpaper/picker/individual/IndividualPickerActivity.java b/src/com/android/wallpaper/picker/individual/IndividualPickerActivity.java
index 1f5dc99..367181a 100755
--- a/src/com/android/wallpaper/picker/individual/IndividualPickerActivity.java
+++ b/src/com/android/wallpaper/picker/individual/IndividualPickerActivity.java
@@ -22,9 +22,6 @@
 import android.content.Intent;
 import android.content.res.Resources.NotFoundException;
 import android.graphics.Insets;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.drawable.Drawable;
-import android.os.Build.VERSION;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.MenuItem;
@@ -33,13 +30,14 @@
 import android.widget.Toast;
 
 import androidx.appcompat.widget.Toolbar;
-import androidx.core.content.ContextCompat;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentManager;
 
 import com.android.wallpaper.R;
 import com.android.wallpaper.compat.BuildCompat;
 import com.android.wallpaper.model.Category;
+import com.android.wallpaper.model.CategoryProvider;
+import com.android.wallpaper.model.CategoryReceiver;
 import com.android.wallpaper.model.InlinePreviewIntentFactory;
 import com.android.wallpaper.model.LiveWallpaperInfo;
 import com.android.wallpaper.model.PickerIntentFactory;
@@ -93,18 +91,29 @@
         mCategoryCollectionId = (savedInstanceState == null)
                 ? getIntent().getStringExtra(EXTRA_CATEGORY_COLLECTION_ID)
                 : savedInstanceState.getString(KEY_CATEGORY_COLLECTION_ID);
-        mCategory = injector.getCategoryProvider(this).getCategory(mCategoryCollectionId);
-        if (mCategory == null) {
-            DiskBasedLogger.e(TAG, "Failed to find the category: " + mCategoryCollectionId, this);
-            // We either were called with an invalid collection Id, or we're restarting with no
-            // saved state, or with a collection id that doesn't exist anymore.
-            // In those cases, we cannot continue, so let's just go back.
-            finish();
-            return;
-        }
+        CategoryProvider categoryProvider = injector.getCategoryProvider(this);
+        categoryProvider.fetchCategories(new CategoryReceiver() {
+            @Override
+            public void onCategoryReceived(Category category) {
+                // Do nothing.
+            }
 
-        setTitle(mCategory.getTitle());
-        getSupportActionBar().setTitle(mCategory.getTitle());
+            @Override
+            public void doneFetchingCategories() {
+                mCategory = categoryProvider.getCategory(mCategoryCollectionId);
+                if (mCategory == null) {
+                    DiskBasedLogger.e(TAG, "Failed to find the category: " + mCategoryCollectionId,
+                            IndividualPickerActivity.this);
+                    // We either were called with an invalid collection Id, or we're restarting with
+                    // no saved state, or with a collection id that doesn't exist anymore.
+                    // In those cases, we cannot continue, so let's just go back.
+                    finish();
+                    return;
+                }
+                onCategoryLoaded();
+            }
+        }, false);
+
         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
 
         toolbar.getNavigationIcon().setTint(getColor(R.color.toolbar_icon_color));
@@ -114,7 +123,8 @@
                 getWindow().getDecorView().getSystemUiVisibility()
                         | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                         | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
-        getWindow().getDecorView().setOnApplyWindowInsetsListener((view, windowInsets) -> {
+        ((View) findViewById(R.id.fragment_container).getParent())
+                .setOnApplyWindowInsetsListener((view, windowInsets) -> {
             view.setPadding(view.getPaddingLeft(), windowInsets.getSystemWindowInsetTop(),
                     view.getPaddingRight(), view.getBottom());
             // Consume only the top inset (status bar), to let other content in the Activity consume
@@ -141,6 +151,11 @@
         }
     }
 
+    private void onCategoryLoaded() {
+        setTitle(mCategory.getTitle());
+        getSupportActionBar().setTitle(mCategory.getTitle());
+    }
+
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         int id = item.getItemId();
diff --git a/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java
index 654ec13..db21da3 100755
--- a/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java
+++ b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java
@@ -48,6 +48,9 @@
 import com.android.wallpaper.asset.Asset;
 import com.android.wallpaper.asset.Asset.DrawableLoadedListener;
 import com.android.wallpaper.config.Flags;
+import com.android.wallpaper.model.Category;
+import com.android.wallpaper.model.CategoryProvider;
+import com.android.wallpaper.model.CategoryReceiver;
 import com.android.wallpaper.model.WallpaperCategory;
 import com.android.wallpaper.model.WallpaperInfo;
 import com.android.wallpaper.model.WallpaperReceiver;
@@ -257,27 +260,38 @@
         mRandom = new Random();
         mHandler = new Handler();
 
-        String collectionId = getArguments().getString(ARG_CATEGORY_COLLECTION_ID);
-        mCategory = (WallpaperCategory) injector.getCategoryProvider(appContext).getCategory(
-                collectionId);
-        if (mCategory == null) {
-            DiskBasedLogger.e(TAG, "Failed to find the category.", appContext);
-
-            // The absence of this category in the CategoryProvider indicates a broken state, probably due
-            // to a relaunch into this activity/fragment following a crash immediately prior; see
-            // b//38030129. Hence, finish the activity and return.
-            getActivity().finish();
-            return;
-        }
-
-        mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
-
         // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
         if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
                 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
             Glide.get(getContext()).clearMemory();
         }
 
+        CategoryProvider categoryProvider = injector.getCategoryProvider(appContext);
+        categoryProvider.fetchCategories(new CategoryReceiver() {
+            @Override
+            public void onCategoryReceived(Category category) {
+                // Do nothing.
+            }
+
+            @Override
+            public void doneFetchingCategories() {
+                mCategory = (WallpaperCategory) categoryProvider.getCategory(
+                        getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
+                if (mCategory == null) {
+                    DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
+
+                    // The absence of this category in the CategoryProvider indicates a broken
+                    // state, see b/38030129. Hence, finish the activity and return.
+                    getActivity().finish();
+                    return;
+                }
+                onCategoryLoaded();
+            }
+        }, false);
+    }
+
+    protected void onCategoryLoaded() {
+        mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
         fetchWallpapers(false);
 
         if (mCategory.supportsThirdParty()) {
@@ -290,6 +304,8 @@
             mPackageStatusNotifier.addListener(mAppStatusListener,
                     WallpaperService.SERVICE_INTERFACE);
         }
+
+        maybeSetUpImageGrid();
     }
 
     void fetchWallpapers(boolean forceReload) {
@@ -346,7 +362,8 @@
         }
         GridMarginDecoration.applyTo(mImageGrid);
 
-        setUpImageGrid();
+        maybeSetUpImageGrid();
+
         setUpBottomSheet();
 
         return view;
@@ -369,6 +386,26 @@
                 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
     }
 
+    private void maybeSetUpImageGrid() {
+        // Skip if mImageGrid been initialized yet
+        if (mImageGrid == null) {
+            return;
+        }
+        // Skip if category hasn't loaded yet
+        if (mCategory == null) {
+            return;
+        }
+        // Skip if the adapter was already created
+        if (mAdapter != null) {
+            return;
+        }
+        setUpImageGrid();
+    }
+
+    /**
+     * Create the adapter and assign it to mImageGrid.
+     * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
+     */
     void setUpImageGrid() {
         mAdapter = new IndividualAdapter(mWallpapers);
         mImageGrid.setAdapter(mAdapter);
@@ -645,7 +682,8 @@
     }
 
     int getNumColumns() {
-        return TileSizeCalculator.getNumIndividualColumns(getActivity());
+        Activity activity = getActivity();
+        return activity == null ? 0 : TileSizeCalculator.getNumIndividualColumns(activity);
     }
 
     /**
diff --git a/src/com/android/wallpaper/util/TileSizeCalculator.java b/src/com/android/wallpaper/util/TileSizeCalculator.java
index f1c76b2..84ecad7 100755
--- a/src/com/android/wallpaper/util/TileSizeCalculator.java
+++ b/src/com/android/wallpaper/util/TileSizeCalculator.java
@@ -28,6 +28,8 @@
 import com.android.wallpaper.module.FormFactorChecker.FormFactor;
 import com.android.wallpaper.module.InjectorProvider;
 
+import androidx.annotation.NonNull;
+
 /**
  * Simple utility class that calculates tile sizes relative to the size of the display.
  */
@@ -65,7 +67,7 @@
      * Returns the number of columns for a grid of category tiles. Selects from fewer and more columns
      * based on the width of the activity.
      */
-    public static int getNumCategoryColumns(Activity activity) {
+    public static int getNumCategoryColumns(@NonNull Activity activity) {
         int windowWidthPx = getActivityWindowWidthPx(activity);
         return getNumCategoryColumns(activity, windowWidthPx);
     }
@@ -74,7 +76,7 @@
      * Returns the number of columns for a grid of individual tiles. Selects from fewer and more
      * columns based on the width of the activity.
      */
-    public static int getNumIndividualColumns(Activity activity) {
+    public static int getNumIndividualColumns(@NonNull Activity activity) {
         int windowWidthPx = getActivityWindowWidthPx(activity);
         return getNumIndividualColumns(activity, windowWidthPx);
     }
@@ -109,7 +111,7 @@
     /**
      * Returns the size of a category grid tile in px.
      */
-    public static Point getCategoryTileSize(Activity activity) {
+    public static Point getCategoryTileSize(@NonNull Activity activity) {
         Context appContext = activity.getApplicationContext();
         int windowWidthPx = getActivityWindowWidthPx(activity);
 
@@ -120,7 +122,7 @@
     /**
      * Returns the size of an individual grid tile for the given activity in px.
      */
-    public static Point getIndividualTileSize(Activity activity) {
+    public static Point getIndividualTileSize(@NonNull Activity activity) {
         Context appContext = activity.getApplicationContext();
         int windowWidthPx = getActivityWindowWidthPx(activity);
 
@@ -133,7 +135,7 @@
      * category or individual tile on any-sized activity on the device. This size matches the
      * individual tile size when an activity takes up the entire screen's width.
      */
-    public static Point getSuggestedThumbnailSize(Context appContext) {
+    public static Point getSuggestedThumbnailSize(@NonNull Context appContext) {
         // Category tiles are larger than individual tiles, so get the number of columns for categories
         // and then calculate a tile size for when the app window takes up the entire display.
         int windowWidthPx = getDeviceDisplayWidthPx(appContext);