Extract TopLevelPickerActivity's logic

Moved all the Wallpaper related logic in TopLevelPickerActivity
(except for desktop-specific), to a delegate, so it can be
reused by ThemePicker.

Bug: 120560581
Change-Id: I15d24fa599a00ebea241ee142584b0ac45276648
diff --git a/src/com/android/wallpaper/picker/TopLevelPickerActivity.java b/src/com/android/wallpaper/picker/TopLevelPickerActivity.java
index 6353bc3..64425e5 100755
--- a/src/com/android/wallpaper/picker/TopLevelPickerActivity.java
+++ b/src/com/android/wallpaper/picker/TopLevelPickerActivity.java
@@ -15,13 +15,10 @@
  */
 package com.android.wallpaper.picker;
 
-import android.Manifest.permission;
 import android.app.Activity;
 import android.app.ProgressDialog;
-import android.app.WallpaperManager;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.PorterDuff.Mode;
@@ -31,7 +28,6 @@
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
-import android.service.wallpaper.WallpaperService;
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -41,28 +37,29 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.Toolbar;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+
 import com.android.wallpaper.R;
 import com.android.wallpaper.asset.Asset;
 import com.android.wallpaper.compat.ButtonDrawableSetterCompat;
-import com.android.wallpaper.compat.WallpaperManagerCompat;
 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.ImageWallpaperInfo;
-import com.android.wallpaper.model.InlinePreviewIntentFactory;
 import com.android.wallpaper.model.WallpaperInfo;
 import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
 import com.android.wallpaper.module.CurrentWallpaperInfoFactory.WallpaperInfoCallback;
 import com.android.wallpaper.module.DailyLoggingAlarmScheduler;
 import com.android.wallpaper.module.ExploreIntentChecker;
 import com.android.wallpaper.module.FormFactorChecker;
-import com.android.wallpaper.module.FormFactorChecker.FormFactor;
 import com.android.wallpaper.module.Injector;
 import com.android.wallpaper.module.InjectorProvider;
 import com.android.wallpaper.module.NetworkStatusNotifier;
 import com.android.wallpaper.module.NetworkStatusNotifier.NetworkStatus;
-import com.android.wallpaper.module.PackageStatusNotifier;
 import com.android.wallpaper.module.UserEventLogger;
 import com.android.wallpaper.module.UserEventLogger.WallpaperSetFailureReason;
 import com.android.wallpaper.module.WallpaperPersister;
@@ -73,11 +70,8 @@
 import com.android.wallpaper.module.WallpaperPreferences.PresentationMode;
 import com.android.wallpaper.module.WallpaperRotationRefresher;
 import com.android.wallpaper.module.WallpaperRotationRefresher.Listener;
-import com.android.wallpaper.picker.PreviewActivity.PreviewActivityIntentFactory;
-import com.android.wallpaper.picker.ViewOnlyPreviewActivity.ViewOnlyPreviewActivityIntentFactory;
+import com.android.wallpaper.picker.CategoryFragment.CategoryFragmentHost;
 import com.android.wallpaper.picker.WallpaperDisabledFragment.WallpaperSupportLevel;
-import com.android.wallpaper.picker.individual.IndividualPickerActivity
-        .IndividualPickerActivityIntentFactory;
 import com.android.wallpaper.picker.individual.IndividualPickerFragment;
 import com.android.wallpaper.util.ScreenSizeCalculator;
 import com.android.wallpaper.util.ThrowableAnalyzer;
@@ -88,26 +82,14 @@
 import com.google.android.material.tabs.TabLayout.OnTabSelectedListener;
 import com.google.android.material.tabs.TabLayout.Tab;
 
-import java.util.ArrayList;
 import java.util.List;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.widget.Toolbar;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
-
 /**
  * Activity allowing users to select a category of wallpapers to choose from.
  */
 public class TopLevelPickerActivity extends BaseActivity implements WallpapersUiContainer,
         CurrentWallpaperBottomSheetPresenter, SetWallpaperErrorDialogFragment.Listener,
-        MyPhotosLauncher {
-    private static final int SHOW_CATEGORY_REQUEST_CODE = 0;
-    private static final int PREVIEW_WALLPAPER_REQUEST_CODE = 1;
-    private static final int VIEW_ONLY_PREVIEW_WALLPAPER_REQUEST_CODE = 2;
-    private static final int READ_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 3;
+        MyPhotosLauncher, CategoryFragmentHost {
 
     private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
             "toplevel_set_wallpaper_error_dialog";
@@ -115,17 +97,11 @@
     private static final String TAG = "TopLevelPicker";
     private static final String KEY_SELECTED_CATEGORY_TAB = "selected_category_tab";
 
-    private IndividualPickerActivityIntentFactory mPickerIntentFactory;
-    private InlinePreviewIntentFactory mPreviewIntentFactory;
-    private InlinePreviewIntentFactory mViewOnlyPreviewIntentFactory;
+    private WallpaperPickerDelegate mDelegate;
     private int mLastSelectedCategoryTabIndex;
-    @FormFactor
-    private int mFormFactor;
-    private WallpaperPreferences mPreferences;
     private UserEventLogger mUserEventLogger;
     private NetworkStatusNotifier mNetworkStatusNotifier;
     private NetworkStatusNotifier.Listener mNetworkStatusListener;
-    private PackageStatusNotifier mPackageStatusNotifier;
     private WallpaperPersister mWallpaperPersister;
     private boolean mWasCustomPhotoWallpaperSet;
     @WallpaperPosition
@@ -157,8 +133,6 @@
     private FrameLayout mLoadingIndicatorContainer;
     private LinearLayout mWallpaperPositionOptions;
 
-    private List<PermissionChangedListener> mPermissionChangedListeners;
-
     /**
      * Staged error dialog fragments that were unable to be shown when the activity didn't allow
      * committing fragment transactions.
@@ -170,9 +144,6 @@
      * retry or re-crop operations.
      */
     private WallpaperInfo mPendingSetWallpaperInfo;
-    private PackageStatusNotifier.Listener mLiveWallpaperStatusListener;
-    private PackageStatusNotifier.Listener mThirdPartyStatusListener;
-    private CategoryProvider mCategoryProvider;
 
     private static int getTextColorIdForWallpaperPositionButton(boolean isSelected) {
         return isSelected ? R.color.accent_color : R.color.material_grey500;
@@ -182,25 +153,16 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mPickerIntentFactory = new IndividualPickerActivityIntentFactory();
-        mPreviewIntentFactory = new PreviewActivityIntentFactory();
-        mViewOnlyPreviewIntentFactory = new ViewOnlyPreviewActivityIntentFactory();
         mLastSelectedCategoryTabIndex = -1;
 
         Injector injector = InjectorProvider.getInjector();
-        mCategoryProvider = injector.getCategoryProvider(this);
-        mPreferences = injector.getPreferences(this);
+        mDelegate = new WallpaperPickerDelegate(this, this, injector);
         mUserEventLogger = injector.getUserEventLogger(this);
         mNetworkStatusNotifier = injector.getNetworkStatusNotifier(this);
-        mPackageStatusNotifier = injector.getPackageStatusNotifier(this);
-        final FormFactorChecker formFactorChecker = injector.getFormFactorChecker(this);
-        mFormFactor = formFactorChecker.getFormFactor();
         mWallpaperPersister = injector.getWallpaperPersister(this);
         mWasCustomPhotoWallpaperSet = false;
 
-        mPermissionChangedListeners = new ArrayList<>();
-
-        @WallpaperSupportLevel int wallpaperSupportLevel = getWallpaperSupportLevel();
+        @WallpaperSupportLevel int wallpaperSupportLevel = mDelegate.getWallpaperSupportLevel();
         if (wallpaperSupportLevel != WallpaperDisabledFragment.SUPPORTED_CAN_SET) {
             setContentView(R.layout.activity_single_fragment);
 
@@ -213,7 +175,7 @@
             return;
         }
 
-        if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
+        if (mDelegate.getFormFactor() == FormFactorChecker.FORM_FACTOR_MOBILE) {
             initializeMobile();
         } else { // DESKTOP
             initializeDesktop(savedInstanceState);
@@ -237,16 +199,11 @@
     @Override
     protected void onDestroy() {
         super.onDestroy();
-
+        mDelegate.cleanUp();
         if (mNetworkStatusListener != null) {
             mNetworkStatusNotifier.unregisterListener(mNetworkStatusListener);
         }
 
-        if (mPackageStatusNotifier != null) {
-            mPackageStatusNotifier.removeListener(mLiveWallpaperStatusListener);
-            mPackageStatusNotifier.removeListener(mThirdPartyStatusListener);
-        }
-
         if (mRefreshWallpaperProgressDialog != null) {
             mRefreshWallpaperProgressDialog.dismiss();
         }
@@ -254,53 +211,27 @@
             mSetWallpaperProgressDialog.dismiss();
         }
 
-        if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && mWasCustomPhotoWallpaperSet) {
+        if (mDelegate.getFormFactor() == FormFactorChecker.FORM_FACTOR_DESKTOP
+                && mWasCustomPhotoWallpaperSet) {
             mUserEventLogger.logWallpaperPosition(mCustomPhotoWallpaperPosition);
         }
     }
 
     @Override
     public void requestCustomPhotoPicker(PermissionChangedListener listener) {
-        if (!isReadExternalStoragePermissionGranted()) {
-            PermissionChangedListener wrappedListener = new PermissionChangedListener() {
-                @Override
-                public void onPermissionsGranted() {
-                    listener.onPermissionsGranted();
-                    showCustomPhotoPicker();
-                }
-
-                @Override
-                public void onPermissionsDenied(boolean dontAskAgain) {
-                    listener.onPermissionsDenied(dontAskAgain);
-                }
-            };
-            requestExternalStoragePermission(wrappedListener);
-
-            return;
-        }
-
-        showCustomPhotoPicker();
+        mDelegate.requestCustomPhotoPicker(listener);
     }
 
-    void requestExternalStoragePermission(PermissionChangedListener listener) {
-        mPermissionChangedListeners.add(listener);
-        requestPermissions(
-                new String[]{permission.READ_EXTERNAL_STORAGE},
-                READ_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
+    @Override
+    public void requestExternalStoragePermission(PermissionChangedListener listener) {
+        mDelegate.requestExternalStoragePermission(listener);
     }
 
     /**
      * Returns whether READ_EXTERNAL_STORAGE has been granted for the application.
      */
-    boolean isReadExternalStoragePermissionGranted() {
-        return getPackageManager().checkPermission(permission.READ_EXTERNAL_STORAGE,
-                getPackageName()) == PackageManager.PERMISSION_GRANTED;
-    }
-
-    private void showCustomPhotoPicker() {
-        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
-        intent.setType("image/*");
-        startActivityForResult(intent, SHOW_CATEGORY_REQUEST_CODE);
+    public boolean isReadExternalStoragePermissionGranted() {
+        return mDelegate.isReadExternalStoragePermissionGranted();
     }
 
     private void initializeMobile() {
@@ -319,7 +250,7 @@
             mUserEventLogger.logAppLaunched();
             DailyLoggingAlarmScheduler.setAlarm(getApplicationContext());
 
-            CategoryPickerFragment newFragment = new CategoryPickerFragment();
+            CategoryFragment newFragment = new CategoryFragment();
             fm.beginTransaction()
                     .add(R.id.fragment_container, newFragment)
                     .commit();
@@ -327,98 +258,7 @@
             forceCategoryRefresh = true;
         }
 
-        populateCategories(-1, forceCategoryRefresh);
-        mLiveWallpaperStatusListener = this::updateLiveWallpapersCategories;
-        mThirdPartyStatusListener = this::updateThirdPartyCategories;
-        mPackageStatusNotifier.addListener(mLiveWallpaperStatusListener,
-                WallpaperService.SERVICE_INTERFACE);
-        mPackageStatusNotifier.addListener(mThirdPartyStatusListener,
-                Intent.ACTION_SET_WALLPAPER);
-    }
-
-    private void updateThirdPartyCategories(String packageName, @PackageStatusNotifier.PackageStatus
-            int status) {
-
-        if (status == PackageStatusNotifier.PackageStatus.ADDED) {
-            mCategoryProvider.fetchCategories(new CategoryReceiver() {
-                @Override
-                public void onCategoryReceived(Category category) {
-                    if (category.supportsThirdParty() && category.containsThirdParty(packageName)) {
-                        addCategory(category, false);
-                    }
-                }
-
-                @Override
-                public void doneFetchingCategories() {
-                    // Do nothing here.
-                }
-            }, true);
-        } else if (status == PackageStatusNotifier.PackageStatus.REMOVED) {
-            Category oldCategory = findThirdPartyCategory(packageName);
-            if (oldCategory != null) {
-                mCategoryProvider.fetchCategories(new CategoryReceiver() {
-                    @Override
-                    public void onCategoryReceived(Category category) {
-                       // Do nothing here
-                    }
-
-                    @Override
-                    public void doneFetchingCategories() {
-                        removeCategory(oldCategory);
-                    }
-                }, true);
-            }
-        } else {
-            // CHANGED package, let's reload all categories as we could have more or fewer now
-            populateCategories(-1, true);
-        }
-    }
-
-    private Category findThirdPartyCategory(String packageName) {
-        int size = mCategoryProvider.getSize();
-        for (int i = 0; i < size; i++) {
-            Category category = mCategoryProvider.getCategory(i);
-            if (category.supportsThirdParty() && category.containsThirdParty(packageName)) {
-                return category;
-            }
-        }
-        return null;
-    }
-
-    private void updateLiveWallpapersCategories(String packageName,
-                                                @PackageStatusNotifier.PackageStatus int status) {
-        String liveWallpaperCollectionId = getString(R.string.live_wallpaper_collection_id);
-        Category oldLiveWallpapersCategory = mCategoryProvider.getCategory(
-                liveWallpaperCollectionId);
-        if (status == PackageStatusNotifier.PackageStatus.REMOVED
-                && (oldLiveWallpapersCategory == null
-                    || !oldLiveWallpapersCategory.containsThirdParty(packageName))) {
-            // If we're removing a wallpaper and the live category didn't contain it already,
-            // there's nothing to do.
-            return;
-        }
-        mCategoryProvider.fetchCategories(new CategoryReceiver() {
-            @Override
-            public void onCategoryReceived(Category category) {
-                // Do nothing here
-            }
-
-            @Override
-            public void doneFetchingCategories() {
-                Category liveWallpapersCategory =
-                        mCategoryProvider.getCategory(liveWallpaperCollectionId);
-                if (liveWallpapersCategory == null) {
-                    // There are no more 3rd party live wallpapers, so the Category is gone.
-                    removeCategory(oldLiveWallpapersCategory);
-                } else {
-                    if (oldLiveWallpapersCategory != null) {
-                        updateCategory(liveWallpapersCategory);
-                    } else {
-                        addCategory(liveWallpapersCategory, false);
-                    }
-                }
-            }
-        }, true);
+        mDelegate.initialize(forceCategoryRefresh);
     }
 
     private void initializeDesktop(Bundle savedInstanceState) {
@@ -446,7 +286,7 @@
             @Override
             public void onTabSelected(Tab tab) {
                 Category category = (Category) tab.getTag();
-                show(category.getCollectionId());
+                showCategoryDesktop(category.getCollectionId());
                 mLastSelectedCategoryTabIndex = tabLayout.getSelectedTabPosition();
             }
 
@@ -508,9 +348,9 @@
                     .commit();
         }
 
-        int selectedTabPosition = savedInstanceState != null
+        mLastSelectedCategoryTabIndex = savedInstanceState != null
                 ? savedInstanceState.getInt(KEY_SELECTED_CATEGORY_TAB) : -1;
-        populateCategories(selectedTabPosition, forceCategoryRefresh);
+        mDelegate.populateCategories(forceCategoryRefresh);
 
         setDesktopLoading(true);
         setUpBottomSheet();
@@ -531,11 +371,11 @@
                 .add(R.id.fragment_container, newFragment)
                 .commit();
 
-        // Reset the last selected category tab index to ensure the app doesn't try to reselect a tab
-        // for a category not yet repopulated.
+        // Reset the last selected category tab index to ensure the app doesn't try to reselect a
+        // tab for a category not yet repopulated.
         mLastSelectedCategoryTabIndex = -1;
 
-        populateCategories(-1 /* selectedTabPosition */, true /* forceCategoryRefresh */);
+        mDelegate.populateCategories(true /* forceCategoryRefresh */);
 
         setDesktopLoading(false);
         setCurrentWallpapersExpanded(false);
@@ -606,7 +446,7 @@
                 } else {
                     alpha = 1f - slideOffset;
                 }
-                LinearLayout bottomSheetContents = (LinearLayout) findViewById(R.id.bottom_sheet_contents);
+                LinearLayout bottomSheetContents = findViewById(R.id.bottom_sheet_contents);
                 bottomSheetContents.setAlpha(alpha);
             }
         });
@@ -729,12 +569,8 @@
                         && presentationMode == WallpaperPreferences.PRESENTATION_MODE_ROTATING;
                 if (showSkipWallpaperButton) {
                     mCurrentWallpaperSkipWallpaperButton.setVisibility(View.VISIBLE);
-                    mCurrentWallpaperSkipWallpaperButton.setOnClickListener(new OnClickListener() {
-                        @Override
-                        public void onClick(View v) {
-                            refreshDailyWallpaper();
-                        }
-                    });
+                    mCurrentWallpaperSkipWallpaperButton.setOnClickListener(
+                            v -> refreshDailyWallpaper());
                 } else {
                     mCurrentWallpaperSkipWallpaperButton.setVisibility(View.GONE);
                 }
@@ -752,7 +588,8 @@
         if (formFactorChecker.getFormFactor() == FormFactorChecker.FORM_FACTOR_DESKTOP) {
             TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
 
-            // tabLayout is only present when the main IndividualPickerFragment is present (as opposed to
+            // tabLayout is only present when the main IndividualPickerFragment is present (as
+            // opposed to
             // the WallpaperDisabledFragment), so need this null check.
             if (tabLayout != null) {
                 savedInstanceState.putInt(KEY_SELECTED_CATEGORY_TAB, tabLayout.getSelectedTabPosition());
@@ -762,74 +599,14 @@
         super.onSaveInstanceState(savedInstanceState);
     }
 
-    /**
-     * Populates the categories appropriately depending on the device form factor.
-     *
-     * @param selectedTabPosition The position of the tab to show as selected, or -1 if no tab
-     *                            should be selected (i.e. because there is no tab layout present, as on MOBILE form factor).
-     * @param forceRefresh        Whether to force a refresh of categories from the CategoryProvider. True if
-     *                            on first launch.
-     */
-    private void populateCategories(final int selectedTabPosition, boolean forceRefresh) {
-
-        final CategoryPickerFragment categoryPickerFragment = getCategoryPickerFragment();
-
-        if (forceRefresh && categoryPickerFragment != null) {
-            categoryPickerFragment.clearCategories();
-        }
-
-        mCategoryProvider.fetchCategories(new CategoryReceiver() {
-            @Override
-            public void onCategoryReceived(Category category) {
-                addCategory(category, true);
-            }
-
-            @Override
-            public void doneFetchingCategories() {
-                if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
-                    notifyDoneFetchingCategories();
-                } else { // DESKTOP
-                    populateCategoryTabs(selectedTabPosition);
-                }
-            }
-        }, forceRefresh);
-    }
-
-    private void notifyDoneFetchingCategories() {
-        CategoryPickerFragment categoryPickerFragment = getCategoryPickerFragment();
-        if (categoryPickerFragment != null) {
-            categoryPickerFragment.doneFetchingCategories();
-        }
-    }
-
-    private void addCategory(Category category, boolean fetchingAll) {
-        CategoryPickerFragment categoryPickerFragment = getCategoryPickerFragment();
-        if (categoryPickerFragment != null) {
-            categoryPickerFragment.addCategory(category, fetchingAll);
-        }
-    }
-
-    private void removeCategory(Category category) {
-        CategoryPickerFragment categoryPickerFragment = getCategoryPickerFragment();
-        if (categoryPickerFragment != null) {
-            categoryPickerFragment.removeCategory(category);
-        }
-    }
-
-    private void updateCategory(Category category) {
-        CategoryPickerFragment categoryPickerFragment = getCategoryPickerFragment();
-        if (categoryPickerFragment != null) {
-            categoryPickerFragment.updateCategory(category);
-        }
-    }
-
+    @Override
     @Nullable
-    private CategoryPickerFragment getCategoryPickerFragment() {
-        if (mFormFactor != FormFactorChecker.FORM_FACTOR_MOBILE) {
+    public CategoryFragment getCategoryFragment() {
+        if (mDelegate.getFormFactor() != FormFactorChecker.FORM_FACTOR_MOBILE) {
             return null;
         }
         FragmentManager fm = getSupportFragmentManager();
-        return (CategoryPickerFragment) fm.findFragmentById(R.id.fragment_container);
+        return (CategoryFragment) fm.findFragmentById(R.id.fragment_container);
     }
 
     /**
@@ -844,12 +621,12 @@
         final TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
         tabLayout.removeAllTabs();
 
-        String currentlySetCollectionId = mPreferences.getHomeWallpaperCollectionId();
+        String currentlySetCollectionId = mDelegate.getPreferences().getHomeWallpaperCollectionId();
 
         Tab tabToSelect = null;
         Tab firstEnumerableCategoryTab = null;
-        for (int i = 0; i < mCategoryProvider.getSize(); i++) {
-            Category category = mCategoryProvider.getCategory(i);
+        for (int i = 0; i < mDelegate.getCategoryProvider().getSize(); i++) {
+            Category category = mDelegate.getCategoryProvider().getCategory(i);
 
             Tab tab = tabLayout.newTab();
             tab.setText(category.getTitle());
@@ -941,23 +718,27 @@
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
 
-        if (requestCode == SHOW_CATEGORY_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+        if (requestCode == WallpaperPickerDelegate.SHOW_CATEGORY_REQUEST_CODE
+                && resultCode == Activity.RESULT_OK) {
             Uri imageUri = (data == null) ? null : data.getData();
             if (imageUri != null) {
-                // User selected an image from the system picker, so launch the preview for that image.
+                // User selected an image from the system picker, so launch the preview for that
+                // image.
                 ImageWallpaperInfo imageWallpaper = new ImageWallpaperInfo(imageUri);
-                if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
+                if (mDelegate.getFormFactor() == FormFactorChecker.FORM_FACTOR_DESKTOP) {
                     setCustomPhotoWallpaper(imageWallpaper);
                     return;
                 }
 
-                imageWallpaper.showPreview(this, mPreviewIntentFactory, PREVIEW_WALLPAPER_REQUEST_CODE);
+                imageWallpaper.showPreview(this, mDelegate.getPreviewIntentFactory(),
+                        WallpaperPickerDelegate.PREVIEW_WALLPAPER_REQUEST_CODE);
             } else {
                 // User finished viewing a category without any data, which implies that the user previewed
                 // and selected a wallpaper in-app, so finish this activity.
                 finishActivityWithResultOk();
             }
-        } else if (requestCode == PREVIEW_WALLPAPER_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+        } else if (requestCode == WallpaperPickerDelegate.PREVIEW_WALLPAPER_REQUEST_CODE
+                && resultCode == Activity.RESULT_OK) {
             // User previewed and selected a wallpaper, so finish this activity.
             finishActivityWithResultOk();
         }
@@ -967,48 +748,21 @@
      * Shows the view-only preview activity for the given wallpaper.
      */
     public void showViewOnlyPreview(WallpaperInfo wallpaperInfo) {
-        wallpaperInfo.showPreview(
-                this, mViewOnlyPreviewIntentFactory, VIEW_ONLY_PREVIEW_WALLPAPER_REQUEST_CODE);
+        mDelegate.showViewOnlyPreview(wallpaperInfo);
     }
 
     @Override
     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                            @NonNull int[] grantResults) {
-        if (requestCode == READ_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE
-                && permissions.length > 0
-                && permissions[0].equals(permission.READ_EXTERNAL_STORAGE)
-                && grantResults.length > 0) {
-            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                for (PermissionChangedListener listener : mPermissionChangedListeners) {
-                    listener.onPermissionsGranted();
-                }
-            } else if (!shouldShowRequestPermissionRationale(permission.READ_EXTERNAL_STORAGE)) {
-                for (PermissionChangedListener listener : mPermissionChangedListeners) {
-                    listener.onPermissionsDenied(true /* dontAskAgain */);
-                }
-            } else {
-                for (PermissionChangedListener listener : mPermissionChangedListeners) {
-                    listener.onPermissionsDenied(false /* dontAskAgain */);
-                }
-            }
-        }
-        mPermissionChangedListeners.clear();
+        mDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
     }
 
     /**
      * Shows the picker activity for the given category.
      */
+    @Override
     public void show(String collectionId) {
-        Category category = findCategoryForCollectionId(collectionId);
-        if (category == null) {
-            return;
-        }
-
-        if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
-            category.show(this, mPickerIntentFactory, SHOW_CATEGORY_REQUEST_CODE);
-        } else { // DESKTOP
-            showCategoryDesktop(collectionId);
-        }
+        mDelegate.show(collectionId);
     }
 
     private void reselectLastTab() {
@@ -1017,20 +771,16 @@
         // In the offline case, "My photos" could be the only category. Thus we need this check --
         // to ensure that we don't try to select the "previously selected" category which was -1.
         if (mLastSelectedCategoryTabIndex > -1) {
-            Tab tabToSelect = tabLayout.getTabAt(mLastSelectedCategoryTabIndex);
+            Tab tabToSelect = tabLayout.getTabAt(
+                    mLastSelectedCategoryTabIndex);
             if (((Category) tabToSelect.getTag()).isEnumerable()) {
                 tabToSelect.select();
             }
         }
     }
 
-    @Nullable
-    private Category findCategoryForCollectionId(String collectionId) {
-        return mCategoryProvider.getCategory(collectionId);
-    }
-
     private void showCategoryDesktop(String collectionId) {
-        Category category = findCategoryForCollectionId(collectionId);
+        Category category = mDelegate.findCategoryForCollectionId(collectionId);
         if (category == null) {
             return;
         }
@@ -1051,7 +801,8 @@
             newFragment.setCurrentWallpaperBottomSheetPresenter(this);
             newFragment.setWallpapersUiContainer(this);
         } else {
-            category.show(this, mPickerIntentFactory, SHOW_CATEGORY_REQUEST_CODE);
+            category.show(this, mDelegate.getPickerIntentFactory(),
+                    WallpaperPickerDelegate.SHOW_CATEGORY_REQUEST_CODE);
 
             // Need to select the tab here in case we are coming back from a "My photos" in which case
             // the tab would have been set to "My photos" while viewing a regular image category.
@@ -1065,36 +816,18 @@
         finish();
     }
 
-    @WallpaperSupportLevel
-    private int getWallpaperSupportLevel() {
-        WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
-
-        if (VERSION.SDK_INT >= VERSION_CODES.N) {
-            if (wallpaperManager.isWallpaperSupported()) {
-                return wallpaperManager.isSetWallpaperAllowed()
-                        ? WallpaperDisabledFragment.SUPPORTED_CAN_SET
-                        : WallpaperDisabledFragment.NOT_SUPPORTED_BLOCKED_BY_ADMIN;
-            }
-            return WallpaperDisabledFragment.NOT_SUPPORTED_BY_DEVICE;
-        } else if (VERSION.SDK_INT >= VERSION_CODES.M) {
-            return wallpaperManager.isWallpaperSupported() ? WallpaperDisabledFragment.SUPPORTED_CAN_SET
-                    : WallpaperDisabledFragment.NOT_SUPPORTED_BY_DEVICE;
-        } else {
-            WallpaperManagerCompat wallpaperManagerCompat =
-                    InjectorProvider.getInjector().getWallpaperManagerCompat(this);
-            boolean isSupported = wallpaperManagerCompat.getDrawable() != null;
-            wallpaperManager.forgetLoadedWallpaper();
-            return isSupported ? WallpaperDisabledFragment.SUPPORTED_CAN_SET
-                    : WallpaperDisabledFragment.NOT_SUPPORTED_BY_DEVICE;
-        }
-    }
-
     @Override
     public void setCurrentWallpapersExpanded(boolean expanded) {
         final BottomSheetBehavior<LinearLayout> bottomSheetBehavior =
                 BottomSheetBehavior.from(mBottomSheet);
         bottomSheetBehavior.setState(
-                expanded ? BottomSheetBehavior.STATE_EXPANDED : BottomSheetBehavior.STATE_COLLAPSED);
+                expanded ? BottomSheetBehavior.STATE_EXPANDED
+                        : BottomSheetBehavior.STATE_COLLAPSED);
+    }
+
+    @Override
+    public void doneFetchingCategories() {
+        populateCategoryTabs(mLastSelectedCategoryTabIndex);
     }
 
     @Override
@@ -1128,7 +861,7 @@
                         dismissSettingWallpaperProgressDialog();
                         refreshCurrentWallpapers(null /* refreshListener */);
 
-                        mPreferences.setPendingWallpaperSetStatus(
+                        mDelegate.getPreferences().setPendingWallpaperSetStatus(
                                 WallpaperPreferences.WALLPAPER_SET_NOT_PENDING);
                         mUserEventLogger.logWallpaperSet(
                                 wallpaper.getCollectionId(getApplicationContext()),
@@ -1154,7 +887,7 @@
                         dismissSettingWallpaperProgressDialog();
                         showSetWallpaperErrorDialog();
 
-                        mPreferences.setPendingWallpaperSetStatus(
+                        mDelegate.getPreferences().setPendingWallpaperSetStatus(
                                 WallpaperPreferences.WALLPAPER_SET_NOT_PENDING);
                         mUserEventLogger.logWallpaperSetResult(
                                 UserEventLogger.WALLPAPER_SET_RESULT_FAILURE);
@@ -1298,8 +1031,8 @@
     }
 
     private void showSettingWallpaperProgressDialog() {
-        // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
-        // causes Espresso to hang once the dialog is shown.
+        // ProgressDialog endlessly updates the UI thread, keeping it from going idle which
+        // therefore causes Espresso to hang once the dialog is shown.
         if (!mTestingMode) {
             int themeResId;
             if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {