Update categories on app install events

Add support for inserting, removing or reloading items
in the category picker activity, as well as in the
LiveWallpaper category based on PackageManager events
for wallpaper apps.

Bug: 79507056

Change-Id: Icf75af9f57229889c79f66339145614a3037bb97
diff --git a/src/com/android/wallpaper/picker/TopLevelPickerActivity.java b/src/com/android/wallpaper/picker/TopLevelPickerActivity.java
index fa46373..85f1a6a 100755
--- a/src/com/android/wallpaper/picker/TopLevelPickerActivity.java
+++ b/src/com/android/wallpaper/picker/TopLevelPickerActivity.java
@@ -26,12 +26,12 @@
 import android.graphics.Point;
 import android.graphics.PorterDuff.Mode;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.VectorDrawable;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
+import android.service.wallpaper.WallpaperService;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.design.widget.BottomSheetBehavior;
@@ -73,6 +73,7 @@
 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;
@@ -114,7 +115,6 @@
     private IndividualPickerActivityIntentFactory mPickerIntentFactory;
     private InlinePreviewIntentFactory mPreviewIntentFactory;
     private InlinePreviewIntentFactory mViewOnlyPreviewIntentFactory;
-    private ArrayList<Category> mCategories;
     private int mLastSelectedCategoryTabIndex;
     @FormFactor
     private int mFormFactor;
@@ -122,6 +122,7 @@
     private UserEventLogger mUserEventLogger;
     private NetworkStatusNotifier mNetworkStatusNotifier;
     private NetworkStatusNotifier.Listener mNetworkStatusListener;
+    private PackageStatusNotifier mPackageStatusNotifier;
     private WallpaperPersister mWallpaperPersister;
     private boolean mWasCustomPhotoWallpaperSet;
     @WallpaperPosition
@@ -166,6 +167,9 @@
      * 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;
@@ -178,13 +182,14 @@
         mPickerIntentFactory = new IndividualPickerActivityIntentFactory();
         mPreviewIntentFactory = new PreviewActivityIntentFactory();
         mViewOnlyPreviewIntentFactory = new ViewOnlyPreviewActivityIntentFactory();
-        mCategories = new ArrayList<>();
         mLastSelectedCategoryTabIndex = -1;
 
         Injector injector = InjectorProvider.getInjector();
+        mCategoryProvider = injector.getCategoryProvider(this);
         mPreferences = injector.getPreferences(this);
         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);
@@ -234,6 +239,11 @@
             mNetworkStatusNotifier.unregisterListener(mNetworkStatusListener);
         }
 
+        if (mPackageStatusNotifier != null) {
+            mPackageStatusNotifier.removeListener(mLiveWallpaperStatusListener);
+            mPackageStatusNotifier.removeListener(mThirdPartyStatusListener);
+        }
+
         if (mRefreshWallpaperProgressDialog != null) {
             mRefreshWallpaperProgressDialog.dismiss();
         }
@@ -315,6 +325,97 @@
         }
 
         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);
     }
 
     private void initializeDesktop(Bundle savedInstanceState) {
@@ -666,40 +767,23 @@
      *                            on first launch.
      */
     private void populateCategories(final int selectedTabPosition, boolean forceRefresh) {
-        mCategories.clear();
 
-        Injector injector = InjectorProvider.getInjector();
-        CategoryProvider categoryProvider = injector.getCategoryProvider(this);
-        categoryProvider.fetchCategories(new CategoryReceiver() {
+        final CategoryPickerFragment categoryPickerFragment = getCategoryPickerFragment();
+
+        if (forceRefresh && categoryPickerFragment != null) {
+            categoryPickerFragment.clearCategories();
+        }
+
+        mCategoryProvider.fetchCategories(new CategoryReceiver() {
             @Override
             public void onCategoryReceived(Category category) {
-                int priority = category.getPriority();
-
-                int index = 0;
-                while (index < mCategories.size() && priority >= mCategories.get(index).getPriority()) {
-                    index++;
-                }
-                mCategories.add(index, category);
-
-                if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
-                    final FragmentManager fm = getSupportFragmentManager();
-                    CategoryPickerFragment categoryPickerFragment =
-                            (CategoryPickerFragment) fm.findFragmentById(R.id.fragment_container);
-                    if (categoryPickerFragment != null) {
-                        categoryPickerFragment.addCategory(category);
-                    }
-                }
+                addCategory(category, true);
             }
 
             @Override
             public void doneFetchingCategories() {
                 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
-                    final FragmentManager fm = getSupportFragmentManager();
-                    CategoryPickerFragment categoryPickerFragment =
-                            (CategoryPickerFragment) fm.findFragmentById(R.id.fragment_container);
-                    if (categoryPickerFragment != null) {
-                        categoryPickerFragment.doneFetchingCategories();
-                    }
+                    notifyDoneFetchingCategories();
                 } else { // DESKTOP
                     populateCategoryTabs(selectedTabPosition);
                 }
@@ -707,6 +791,43 @@
         }, 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);
+        }
+    }
+
+    @Nullable
+    private CategoryPickerFragment getCategoryPickerFragment() {
+        if (mFormFactor != FormFactorChecker.FORM_FACTOR_MOBILE) {
+            return null;
+        }
+        FragmentManager fm = getSupportFragmentManager();
+        return (CategoryPickerFragment) fm.findFragmentById(R.id.fragment_container);
+    }
+
     /**
      * Populates the category tabs on DESKTOP form factor.
      *
@@ -723,8 +844,8 @@
 
         Tab tabToSelect = null;
         Tab firstEnumerableCategoryTab = null;
-        for (int i = 0; i < mCategories.size(); i++) {
-            Category category = mCategories.get(i);
+        for (int i = 0; i < mCategoryProvider.getSize(); i++) {
+            Category category = mCategoryProvider.getCategory(i);
 
             Tab tab = tabLayout.newTab();
             tab.setText(category.getTitle());
@@ -901,12 +1022,7 @@
 
     @Nullable
     private Category findCategoryForCollectionId(String collectionId) {
-        for (Category category : mCategories) {
-            if (category.getCollectionId().equals(collectionId)) {
-                return category;
-            }
-        }
-        return null;
+        return mCategoryProvider.getCategory(collectionId);
     }
 
     private void showCategoryDesktop(String collectionId) {