From 342e9df03c4c154a00fc14dd774caa66f4376add Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Wed, 10 Mar 2021 11:02:19 -0600 Subject: [PATCH] [Search]Implement search session cache in AllApps - Skips SearchTarget reapplication if the id has not changed - Resolves Incessant subtitle on short row Bug: 181510793 Test: Manual Change-Id: Ic10ef9682fd3700616b7b1b7a60e82b5f2250fd3 --- src/com/android/launcher3/Launcher.java | 10 - .../allapps/search/LiveSearchManager.java | 325 ------------------ .../android/launcher3/icons/IconCache.java | 11 +- 3 files changed, 7 insertions(+), 339 deletions(-) delete mode 100644 src/com/android/launcher3/allapps/search/LiveSearchManager.java diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 1546ee3572..c57f62171e 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -113,7 +113,6 @@ import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.DiscoveryBounce; -import com.android.launcher3.allapps.search.LiveSearchManager; import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; @@ -277,8 +276,6 @@ public class Launcher extends StatefulActivity implements Launche private Configuration mOldConfig; - private LiveSearchManager mLiveSearchManager; - @Thunk Workspace mWorkspace; @Thunk @@ -401,8 +398,6 @@ public class Launcher extends StatefulActivity implements Launche mAllAppsController = new AllAppsTransitionController(this); mStateManager = new StateManager<>(this, NORMAL); - mLiveSearchManager = new LiveSearchManager(this); - mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs); mAppWidgetManager = new WidgetManagerHelper(this); @@ -490,10 +485,6 @@ public class Launcher extends StatefulActivity implements Launche } } - public LiveSearchManager getLiveSearchManager() { - return mLiveSearchManager; - } - protected LauncherOverlayManager getDefaultOverlay() { return new LauncherOverlayManager() { }; } @@ -1594,7 +1585,6 @@ public class Launcher extends StatefulActivity implements Launche mOverlayManager.onActivityDestroyed(this); mUserChangedCallbackCloseable.close(); - mLiveSearchManager.stop(); } public LauncherAccessibilityDelegate getAccessibilityDelegate() { diff --git a/src/com/android/launcher3/allapps/search/LiveSearchManager.java b/src/com/android/launcher3/allapps/search/LiveSearchManager.java deleted file mode 100644 index adb882a90a..0000000000 --- a/src/com/android/launcher3/allapps/search/LiveSearchManager.java +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.allapps.search; - -import static com.android.launcher3.LauncherState.ALL_APPS; -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; -import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR; -import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; -import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget; - -import android.app.Activity; -import android.app.Application.ActivityLifecycleCallbacks; -import android.appwidget.AppWidgetHost; -import android.appwidget.AppWidgetHostView; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProviderInfo; -import android.content.ComponentName; -import android.content.Context; -import android.net.Uri; -import android.os.Bundle; -import android.os.UserHandle; -import android.util.Log; - -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; -import androidx.annotation.WorkerThread; -import androidx.lifecycle.Observer; -import androidx.slice.Slice; -import androidx.slice.SliceViewManager; -import androidx.slice.SliceViewManager.SliceCallback; - -import com.android.launcher3.Alarm; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherState; -import com.android.launcher3.statemanager.StateManager.StateListener; -import com.android.launcher3.util.ComponentKey; -import com.android.launcher3.util.SafeCloseable; -import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; -import com.android.launcher3.widget.PendingAddWidgetInfo; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.function.Consumer; - -/** - * Manages Lifecycle for Live search results - */ -public class LiveSearchManager implements StateListener { - - private static final String TAG = "LiveSearchManager"; - - private static final long SLICE_TIMEOUT_MS = 50; - public static final int SEARCH_APPWIDGET_HOST_ID = 2048; - - private final Launcher mLauncher; - private final HashMap mUriSliceMap = new HashMap<>(); - - private final HashMap mWidgetPlaceholders = - new HashMap<>(); - private SearchWidgetHost mSearchWidgetHost; - - public LiveSearchManager(Launcher launcher) { - mLauncher = launcher; - mLauncher.getStateManager().addStateListener(this); - } - - /** - * Creates new {@link AppWidgetHostView} from {@link AppWidgetProviderInfo}. Caches views for - * quicker result within the same search session - */ - public SearchWidgetInfoContainer getPlaceHolderWidget(AppWidgetProviderInfo providerInfo) { - if (mSearchWidgetHost == null) { - mSearchWidgetHost = new SearchWidgetHost(mLauncher); - mSearchWidgetHost.startListening(); - } - - ComponentName provider = providerInfo.provider; - UserHandle userHandle = providerInfo.getProfile(); - - ComponentKey key = new ComponentKey(provider, userHandle); - if (mWidgetPlaceholders.containsKey(key)) { - return mWidgetPlaceholders.get(key); - } - - LauncherAppWidgetProviderInfo pinfo = LauncherAppWidgetProviderInfo.fromProviderInfo( - mLauncher, providerInfo); - PendingAddWidgetInfo pendingAddWidgetInfo = new PendingAddWidgetInfo(pinfo); - - Bundle options = getDefaultOptionsForWidget(mLauncher, pendingAddWidgetInfo); - int appWidgetId = mSearchWidgetHost.allocateAppWidgetId(); - boolean success = AppWidgetManager.getInstance(mLauncher) - .bindAppWidgetIdIfAllowed(appWidgetId, userHandle, provider, options); - if (!success) { - mSearchWidgetHost.deleteAppWidgetId(appWidgetId); - mWidgetPlaceholders.put(key, null); - return null; - } - - SearchWidgetInfoContainer view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView( - mLauncher, appWidgetId, providerInfo); - view.setTag(pendingAddWidgetInfo); - mWidgetPlaceholders.put(key, view); - return view; - } - - /** - * Stop search session - */ - public void stop() { - clearWidgetHost(); - } - - private void clearWidgetHost() { - if (mSearchWidgetHost != null) { - mSearchWidgetHost.stopListening(); - mSearchWidgetHost.clearViews(); - mSearchWidgetHost.deleteHost(); - mWidgetPlaceholders.clear(); - mSearchWidgetHost = null; - } - } - - @Override - public void onStateTransitionComplete(LauncherState finalState) { - if (finalState != ALL_APPS) { - // Clear all search session related objects - mUriSliceMap.values().forEach(SliceLifeCycle::destroy); - mUriSliceMap.clear(); - - clearWidgetHost(); - } - } - - /** - * Adds a new observer for the provided uri and returns a callback to cancel this observer - */ - public SafeCloseable addObserver(Uri uri, Observer listener, - Consumer timeoutConsumer) { - SliceLifeCycle slc = mUriSliceMap.get(uri); - if (slc == null) { - slc = new SliceLifeCycle(uri, mLauncher); - mUriSliceMap.put(uri, slc); - } - if (slc.mLastValue != null) { - listener.onChanged(slc.mLastValue); - } - - // Use a listener wrapper to handle error timeout. - Observer listenerWrapper = new Observer() { - final Alarm mErrorTimeout = new Alarm(); - { - mErrorTimeout.setOnAlarmListener(alarm -> { - alarm.cancelAlarm(); - timeoutConsumer.accept(uri); - }); - mErrorTimeout.setAlarm(SLICE_TIMEOUT_MS); - } - - @Override - public void onChanged(Slice slice) { - if (slice == null) { - return; - } - - if (mErrorTimeout.alarmPending()) { - mErrorTimeout.cancelAlarm(); - } - - if (mUriSliceMap.get(uri) != null) { - mUriSliceMap.get(uri).mLastValue = slice; - } - - listener.onChanged(slice); - } - }; - - slc.addListener(listenerWrapper); - - final SliceLifeCycle sliceLifeCycle = slc; - return () -> sliceLifeCycle.removeListener(listenerWrapper); - } - - static class SearchWidgetHost extends AppWidgetHost { - SearchWidgetHost(Context context) { - super(context, SEARCH_APPWIDGET_HOST_ID); - } - - @Override - protected AppWidgetHostView onCreateView(Context context, int appWidgetId, - AppWidgetProviderInfo appWidget) { - return new SearchWidgetInfoContainer(context); - } - - @Override - public void clearViews() { - super.clearViews(); - } - } - - private static class SliceLifeCycle - implements ActivityLifecycleCallbacks, SliceCallback { - - private final Uri mUri; - private final Launcher mLauncher; - private final SliceViewManager mSliceViewManager; - private final ArrayList> mListeners = new ArrayList<>(); - - private boolean mDestroyed = false; - private boolean mWasListening = false; - - Slice mLastValue; - - SliceLifeCycle(Uri uri, Launcher launcher) { - mUri = uri; - mLauncher = launcher; - mSliceViewManager = SliceViewManager.getInstance(launcher); - launcher.registerActivityLifecycleCallbacks(this); - - if (launcher.isDestroyed()) { - onActivityDestroyed(launcher); - } else if (launcher.isStarted()) { - onActivityStarted(launcher); - } - } - - @Override - public void onActivityDestroyed(Activity activity) { - destroy(); - } - - @Override - public void onActivityStarted(Activity activity) { - updateListening(); - } - - @Override - public void onActivityStopped(Activity activity) { - updateListening(); - } - - private void updateListening() { - boolean isListening = mDestroyed - ? false - : (mLauncher.isStarted() && !mListeners.isEmpty()); - UI_HELPER_EXECUTOR.execute(() -> uploadListeningBg(isListening)); - } - - @WorkerThread - private void uploadListeningBg(boolean isListening) { - if (mWasListening != isListening) { - mWasListening = isListening; - if (isListening) { - mSliceViewManager.registerSliceCallback(mUri, MAIN_EXECUTOR, this); - // Update slice one-time on the different thread so that we can display - // multiple slices in parallel - THREAD_POOL_EXECUTOR.execute(this::updateSlice); - } else { - mSliceViewManager.unregisterSliceCallback(mUri, this); - } - } - } - - @UiThread - private void addListener(Observer listener) { - mListeners.add(listener); - updateListening(); - } - - @UiThread - private void removeListener(Observer listener) { - mListeners.remove(listener); - updateListening(); - } - - @WorkerThread - private void updateSlice() { - try { - Slice s = mSliceViewManager.bindSlice(mUri); - MAIN_EXECUTOR.execute(() -> onSliceUpdated(s)); - } catch (Exception e) { - Log.d(TAG, "Error fetching slice", e); - } - } - - @UiThread - @Override - public void onSliceUpdated(@Nullable Slice s) { - mListeners.forEach(l -> l.onChanged(s)); - } - - private void destroy() { - if (mDestroyed) { - return; - } - mDestroyed = true; - mLauncher.unregisterActivityLifecycleCallbacks(this); - mListeners.clear(); - } - - @Override - public void onActivityCreated(Activity activity, Bundle bundle) { } - - @Override - public void onActivityPaused(Activity activity) { } - - @Override - public void onActivityResumed(Activity activity) { } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { } - } -} diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java index 61f2c2adae..988794ccb6 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -131,6 +131,7 @@ public class IconCache extends BaseIconCache { /** * Fetches high-res icon for the provided ItemInfo and updates the caller when done. + * * @return a request ID that can be used to cancel the request. */ public HandlerRunnable updateIconInBackground(final ItemInfoUpdateReceiver caller, @@ -139,7 +140,7 @@ public class IconCache extends BaseIconCache { if (mPendingIconRequestCount <= 0) { MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); } - mPendingIconRequestCount ++; + mPendingIconRequestCount++; HandlerRunnable request = new HandlerRunnable<>(mWorkerHandler, () -> { @@ -158,7 +159,7 @@ public class IconCache extends BaseIconCache { } private void onIconRequestEnd() { - mPendingIconRequestCount --; + mPendingIconRequestCount--; if (mPendingIconRequestCount <= 0) { MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } @@ -289,7 +290,8 @@ public class IconCache extends BaseIconCache { @NonNull Supplier activityInfoProvider, boolean usePkgIcon, boolean useLowResIcon) { CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), infoInOut.user, - activityInfoProvider, mLauncherActivityInfoCachingLogic, usePkgIcon, useLowResIcon); + activityInfoProvider, mLauncherActivityInfoCachingLogic, usePkgIcon, + useLowResIcon); applyCacheEntry(entry, infoInOut); } @@ -315,7 +317,8 @@ public class IconCache extends BaseIconCache { } public void updateSessionCache(PackageUserKey key, PackageInstaller.SessionInfo info) { - cachePackageInstallInfo(key.mPackageName, key.mUser, info.getAppIcon(), info.getAppLabel()); + cachePackageInstallInfo(key.mPackageName, key.mUser, info.getAppIcon(), + info.getAppLabel()); } @Override