Merge "Make THEME_CATEGORIES public" into sc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d5f3c8b..68eb4be 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -8,7 +8,31 @@
     <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
     <uses-permission android:name="android.permission.SET_WALLPAPER_COMPONENT" />
-    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
+    <queries>
+        <!-- Specific intents Wallpaper picker query for -->
+        <!-- Intent filter with action SET_WALLPAPER -->
+        <intent>
+            <action android:name="android.intent.action.SET_WALLPAPER" />
+        </intent>
+        <!-- Intent filter with action GET_CONTENT and data's mimeType as "image/*" -->
+        <intent>
+            <action android:name="android.intent.action.GET_CONTENT" />
+            <data android:mimeType="image/*" />
+        </intent>
+        <!-- Intent filter with action VIEW -->
+        <intent>
+            <action android:name="android.intent.action.VIEW" />
+        </intent>
+        <!-- Intent filter with action WallpaperService (live wallpaper interface) -->
+        <intent>
+            <action android:name="android.service.wallpaper.WallpaperService" />
+        </intent>
+        <!-- Intent filter with action used to discover partner -->
+        <intent>
+            <action android:name="com.android.launcher3.action.PARTNER_CUSTOMIZATION" />
+        </intent>
+    </queries>
 
     <application
         tools:replace="android:icon,android:name"
diff --git a/res/layout/fragment_grid_picker.xml b/res/layout/fragment_grid_picker.xml
index f34d013..57aee6c 100644
--- a/res/layout/fragment_grid_picker.xml
+++ b/res/layout/fragment_grid_picker.xml
@@ -51,7 +51,7 @@
                 android:id="@+id/options_container"
                 android:layout_width="match_parent"
                 android:layout_height="@dimen/options_container_height"
-
+                android:layout_marginBottom="@dimen/options_container_bottom_margin"
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintStart_toStartOf="parent" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index e5bba8c..37718ec 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -31,6 +31,7 @@
     <!-- Dimensions for the customization option tiles -->
     <dimen name="options_container_height">124dp</dimen>
     <dimen name="options_container_width">0dp</dimen>
+    <dimen name="options_container_bottom_margin">12dp</dimen>
     <dimen name="option_tile_width">88dp</dimen>
     <dimen name="option_icon_size">16dp</dimen>
     <dimen name="theme_option_icon_sample_height">22dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 406a637..40f8f06 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -32,6 +32,7 @@
     </style>
 
     <style name="CustomizationTheme.NoActionBar">
+        <item name="android:navigationBarColor">@android:color/transparent</item>
         <item name="android:windowActionBar">false</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:fitsSystemWindows">false</item>
diff --git a/src/com/android/customization/picker/CustomizationPickerActivity.java b/src/com/android/customization/picker/CustomizationPickerActivity.java
index 69aa428..1d6fa9b 100644
--- a/src/com/android/customization/picker/CustomizationPickerActivity.java
+++ b/src/com/android/customization/picker/CustomizationPickerActivity.java
@@ -411,13 +411,8 @@
     }
 
     @Override
-    public boolean isNavigationTabsContained() {
-        return true;
-    }
-
-    @Override
     public void fetchCategories() {
-        mDelegate.initialize(false);
+        mDelegate.initialize(!mDelegate.getCategoryProvider().isCategoriesFetched());
     }
 
     @Override
diff --git a/src/com/android/customization/picker/grid/GridFragment.java b/src/com/android/customization/picker/grid/GridFragment.java
index d29627e..d9b4035 100644
--- a/src/com/android/customization/picker/grid/GridFragment.java
+++ b/src/com/android/customization/picker/grid/GridFragment.java
@@ -116,6 +116,16 @@
         mLoading = view.findViewById(R.id.loading_indicator);
         mError = view.findViewById(R.id.error_section);
 
+        // For nav bar edge-to-edge effect.
+        view.setOnApplyWindowInsetsListener((v, windowInsets) -> {
+            v.setPadding(
+                    v.getPaddingLeft(),
+                    v.getPaddingTop(),
+                    v.getPaddingRight(),
+                    windowInsets.getSystemWindowInsetBottom());
+            return windowInsets.consumeSystemWindowInsets();
+        });
+
         // Clear memory cache whenever grid fragment view is being loaded.
         Glide.get(getContext()).clearMemory();
 
diff --git a/src/com/android/customization/widget/OptionSelectorController.java b/src/com/android/customization/widget/OptionSelectorController.java
index 1d1b658..4cbb977 100644
--- a/src/com/android/customization/widget/OptionSelectorController.java
+++ b/src/com/android/customization/widget/OptionSelectorController.java
@@ -80,8 +80,8 @@
 
     private final Set<OptionSelectedListener> mListeners = new HashSet<>();
     private RecyclerView.Adapter<TileViewHolder> mAdapter;
-    private CustomizationOption mSelectedOption;
-    private CustomizationOption mAppliedOption;
+    private T mSelectedOption;
+    private T mAppliedOption;
 
     public OptionSelectorController(RecyclerView container, List<T> options) {
         this(container, options, true, CheckmarkStyle.CORNER);
@@ -103,7 +103,10 @@
         mListeners.remove(listener);
     }
 
-    public void setSelectedOption(CustomizationOption option) {
+    /**
+     * Mark the given option as selected
+     */
+    public void setSelectedOption(T option) {
         if (!mOptions.contains(option)) {
             throw new IllegalArgumentException("Invalid option");
         }
@@ -114,15 +117,22 @@
     }
 
     /**
+     * @return whether this controller contains the given option
+     */
+    public boolean containsOption(T option) {
+        return mOptions.contains(option);
+    }
+
+    /**
      * Mark an option as the one which is currently applied on the device. This will result in a
      * check being displayed in the lower-right corner of the corresponding ViewHolder.
      * @param option
      */
-    public void setAppliedOption(CustomizationOption option) {
+    public void setAppliedOption(T option) {
         if (!mOptions.contains(option)) {
             throw new IllegalArgumentException("Invalid option");
         }
-        CustomizationOption lastAppliedOption = mAppliedOption;
+        T lastAppliedOption = mAppliedOption;
         mAppliedOption = option;
         mAdapter.notifyItemChanged(mOptions.indexOf(option));
         if (lastAppliedOption != null) {
@@ -130,7 +140,7 @@
         }
     }
 
-    private void updateActivatedStatus(CustomizationOption option, boolean isActivated) {
+    private void updateActivatedStatus(T option, boolean isActivated) {
         int index = mOptions.indexOf(option);
         if (index < 0) {
             return;
@@ -195,7 +205,7 @@
 
             @Override
             public void onBindViewHolder(@NonNull TileViewHolder holder, int position) {
-                CustomizationOption option = mOptions.get(position);
+                T option = mOptions.get(position);
                 if (option.isActive(manager)) {
                     mAppliedOption = option;
                     if (mSelectedOption == null) {
@@ -304,8 +314,6 @@
                 ((GridLayoutManager) mContainer.getLayoutManager()).setSpanCount(numColumns);
             }
 
-            int containerSidePadding = (extraSpace / (numColumns + 1)) / 2;
-            mContainer.setPaddingRelative(containerSidePadding, 0, containerSidePadding, 0);
             mContainer.setOverScrollMode(View.OVER_SCROLL_NEVER);
             return;
         }
@@ -314,7 +322,7 @@
         if (extraSpace >= 0) {
             mContainer.setOverScrollMode(View.OVER_SCROLL_NEVER);
         }
-        int itemSideMargin =  res.getDimensionPixelOffset(R.dimen.option_tile_margin_horizontal);
+        int itemSideMargin = res.getDimensionPixelOffset(R.dimen.option_tile_margin_horizontal);
         int defaultTotalPadding = itemSideMargin * (mAdapter.getItemCount() * 2 + 2);
         if (extraSpace > defaultTotalPadding) {
             int spaceBetweenItems = extraSpace / (mAdapter.getItemCount() + 1);
@@ -333,7 +341,7 @@
         if (mListeners.isEmpty()) {
             return;
         }
-        CustomizationOption option = mSelectedOption;
+        T option = mSelectedOption;
         Set<OptionSelectedListener> iterableListeners = new HashSet<>(mListeners);
         for (OptionSelectedListener listener : iterableListeners) {
             listener.onOptionSelected(option);
@@ -359,7 +367,7 @@
          * @param option The customization option
          * @param id Resource ID of the string to use for the content description
          */
-        public void setContentDescription(Context context, CustomizationOption option, int id) {
+        public void setContentDescription(Context context, CustomizationOption<?> option, int id) {
             title = option.getTitle();
             if (TextUtils.isEmpty(title) && tileView != null) {
                 title = tileView.getContentDescription();