diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java index c441e2291b..4bf34324a7 100644 --- a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java +++ b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java @@ -15,28 +15,22 @@ */ package com.android.launcher3.search; -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; -import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; - import android.app.search.SearchTarget; import android.app.search.SearchTargetEvent; import android.content.Context; import android.util.AttributeSet; -import android.util.Log; import android.widget.LinearLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.lifecycle.LiveData; -import androidx.slice.Slice; import androidx.slice.SliceItem; import androidx.slice.widget.EventInfo; import androidx.slice.widget.SliceView; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.model.data.PackageItemInfo; +import com.android.launcher3.util.SafeCloseable; import java.util.ArrayList; import java.util.List; @@ -47,13 +41,11 @@ import java.util.List; public class SearchResultIconSlice extends LinearLayout implements SearchTargetHandler, SliceView.OnSliceActionListener { - private static final String TAG = "SearchSliceController"; - private final Launcher mLauncher; private SliceView mSliceView; private SearchResultIcon mIcon; - private LiveData mSliceLiveData; + private SafeCloseable mSliceSession; private String mTargetId; public SearchResultIconSlice(Context context) { @@ -87,26 +79,20 @@ public class SearchResultIconSlice extends LinearLayout implements SearchTargetH mTargetId = parentTarget.getId(); reset(); updateIcon(parentTarget, children); - try { - mSliceLiveData = mLauncher.getLiveSearchManager().getSliceForUri( - parentTarget.getSliceUri()); - mSliceLiveData.observe(mLauncher, mSliceView); - } catch (Exception ex) { - Log.e(TAG, "unable to bind slice", ex); - } + mSliceSession = mLauncher.getLiveSearchManager() + .addObserver(parentTarget.getSliceUri(), mSliceView); } private void updateIcon(SearchTarget parentTarget, List children) { if (children.size() == 1) { mIcon.apply(children.get(0), new ArrayList<>()); } else { - LauncherAppState appState = LauncherAppState.getInstance(getContext()); - MODEL_EXECUTOR.post(() -> { - PackageItemInfo pkgItem = new PackageItemInfo(parentTarget.getPackageName()); - pkgItem.user = parentTarget.getUserHandle(); - appState.getIconCache().getTitleAndIconForApp(pkgItem, false); - MAIN_EXECUTOR.post(() -> mIcon.applyFromItemInfoWithIcon(pkgItem)); - }); + PackageItemInfo pkgItem = new PackageItemInfo(parentTarget.getPackageName()); + pkgItem.user = parentTarget.getUserHandle(); + if (!pkgItem.equals(mIcon.getTag())) { + // The icon will load and apply high res icon automatically + mIcon.applyFromItemInfoWithIcon(pkgItem); + } } } @@ -124,8 +110,8 @@ public class SearchResultIconSlice extends LinearLayout implements SearchTargetH private void reset() { mSliceView.setOnSliceActionListener(null); - if (mSliceLiveData != null) { - mSliceLiveData.removeObservers(mLauncher); + if (mSliceSession != null) { + mSliceSession.close(); } } diff --git a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java index 4c64265cc3..e22f6ab992 100644 --- a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java +++ b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java @@ -15,6 +15,9 @@ */ package com.android.launcher3.search; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.app.search.SearchTarget; import android.app.search.SearchTargetEvent; import android.appwidget.AppWidgetHostView; @@ -36,16 +39,19 @@ import com.android.launcher3.BubbleTextView; import com.android.launcher3.CheckLongPressHelper; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.allapps.search.SearchWidgetInfoContainer; import com.android.launcher3.dragndrop.DraggableView; +import com.android.launcher3.icons.cache.HandlerRunnable; import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.widget.PendingAddWidgetInfo; import java.util.List; - /** * displays live version of a widget upon receiving {@link AppWidgetProviderInfo} from Search * provider @@ -63,6 +69,7 @@ public class SearchResultWidget extends LinearLayout implements SearchTargetHand private final float mScaleToFit; private SearchWidgetInfoContainer mInfoContainer; + private HandlerRunnable mLabelRequest; private BubbleTextView mWidgetProvider; private TextView mWidgetLabel; @@ -124,11 +131,18 @@ public class SearchResultWidget extends LinearLayout implements SearchTargetHand } private void showWidgetInfo(AppWidgetProviderInfo providerInfo) { - String title = providerInfo.loadLabel(mLauncher.getPackageManager()); PackageItemInfo pinfo = new PackageItemInfo(providerInfo.provider.getPackageName()); pinfo.user = providerInfo.getProfile(); mWidgetProvider.applyFromItemInfoWithIcon(pinfo); - mWidgetLabel.setText(title); + + mLabelRequest = new HandlerRunnable<>( + MODEL_EXECUTOR.getHandler(), + () -> LauncherAppState.getInstance(mLauncher).getIconCache() + .getTitleNoCache(LauncherAppWidgetProviderInfo + .fromProviderInfo(mLauncher, providerInfo)), + MAIN_EXECUTOR, + mWidgetLabel::setText); + Utilities.postAsyncCallback(MODEL_EXECUTOR.getHandler(), mLabelRequest); } /** @@ -137,7 +151,18 @@ public class SearchResultWidget extends LinearLayout implements SearchTargetHand public void removeListener() { if (mInfoContainer != null) { mInfoContainer.detachWidget(mHostView); + mInfoContainer = null; } + if (mLabelRequest != null) { + mLabelRequest.cancel(); + mLabelRequest = null; + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + removeListener(); } private void reportEvent(int eventType) { diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 5007ffc7be..2367249163 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -66,9 +66,9 @@ import com.android.launcher3.graphics.IconShape; import com.android.launcher3.graphics.PlaceHolderIconDrawable; import com.android.launcher3.graphics.PreloadIconDrawable; import com.android.launcher3.icons.DotRenderer; -import com.android.launcher3.icons.IconCache.IconLoadRequest; import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; import com.android.launcher3.icons.LauncherIcons; +import com.android.launcher3.icons.cache.HandlerRunnable; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; @@ -171,7 +171,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, @ViewDebug.ExportedProperty(category = "launcher") private boolean mDisableRelayout = false; - private IconLoadRequest mIconLoadRequest; + private HandlerRunnable mIconLoadRequest; private boolean mEnableIconUpdateAnimation = false; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2334267fd9..36963f19dd 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -98,13 +98,9 @@ import android.widget.ImageView; import android.widget.Toast; import androidx.annotation.CallSuper; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.annotation.VisibleForTesting; -import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.LifecycleOwner; -import androidx.lifecycle.LifecycleRegistry; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; @@ -211,8 +207,7 @@ import java.util.stream.Stream; * Default launcher application. */ public class Launcher extends StatefulActivity implements LauncherExterns, - Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener, - LifecycleOwner { + Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener { public static final String TAG = "Launcher"; public static final ActivityTracker ACTIVITY_TRACKER = new ActivityTracker<>(); @@ -275,8 +270,6 @@ public class Launcher extends StatefulActivity implements Launche private LauncherAppTransitionManager mAppTransitionManager; private Configuration mOldConfig; - private LifecycleRegistry mLifecycleRegistry; - private LiveSearchManager mLiveSearchManager; @Thunk @@ -392,12 +385,12 @@ public class Launcher extends StatefulActivity implements Launche mIconCache = app.getIconCache(); mAccessibilityDelegate = new LauncherAccessibilityDelegate(this); - mLiveSearchManager = new LiveSearchManager(this); - mDragController = new DragController(this); mAllAppsController = new AllAppsTransitionController(this); mStateManager = new StateManager<>(this, NORMAL); + mLiveSearchManager = new LiveSearchManager(this); + mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs); mAppWidgetManager = new WidgetManagerHelper(this); @@ -486,15 +479,6 @@ public class Launcher extends StatefulActivity implements Launche if (Utilities.ATLEAST_R) { getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING); } - - mLifecycleRegistry = new LifecycleRegistry(this); - mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED); - } - - @NonNull - @Override - public Lifecycle getLifecycle() { - return mLifecycleRegistry; } public LiveSearchManager getLiveSearchManager() { @@ -913,7 +897,6 @@ public class Launcher extends StatefulActivity implements Launche @Override protected void onStop() { - mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED); super.onStop(); if (mDeferOverlayCallbacks) { checkIfOverlayStillDeferred(); @@ -937,7 +920,6 @@ public class Launcher extends StatefulActivity implements Launche mAppWidgetHost.setListenIfResumed(true); TraceHelper.INSTANCE.endSection(traceToken); - mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED); } @Override @@ -1091,7 +1073,6 @@ public class Launcher extends StatefulActivity implements Launche } TraceHelper.INSTANCE.endSection(traceToken); - mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED); } @Override @@ -1099,7 +1080,6 @@ public class Launcher extends StatefulActivity implements Launche // Ensure that items added to Launcher are queued until Launcher returns ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED); - mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED); super.onPause(); mDragController.cancelDrag(); mLastTouchUpTime = -1; @@ -1598,7 +1578,6 @@ public class Launcher extends StatefulActivity implements Launche mAppTransitionManager.unregisterRemoteAnimations(); mAppTransitionManager.unregisterRemoteTransitions(); mUserChangedCallbackCloseable.close(); - mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED); mLiveSearchManager.stop(); } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 2f805fd550..a4e1f271fb 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -268,13 +268,10 @@ public class AllAppsTransitionController implements StateHandler, && !FeatureFlags.DISABLE_INITIAL_IME_IN_ALLAPPS.get() && BuildCompat.isAtLeastR()) { mInsetController.onAnimationEnd(mProgress); if (Float.compare(mProgress, 0f) == 0) { - mLauncher.getLiveSearchManager().start(); EditText editText = mAppsView.getSearchUiManager().getEditText(); if (editText != null && !mInsetController.showSearchEduIfNecessary()) { editText.requestFocus(); } - } else { - mLauncher.getLiveSearchManager().stop(); } // TODO: should make the controller hide synchronously } diff --git a/src/com/android/launcher3/allapps/search/LiveSearchManager.java b/src/com/android/launcher3/allapps/search/LiveSearchManager.java index c2f0b960c8..71aedb8272 100644 --- a/src/com/android/launcher3/allapps/search/LiveSearchManager.java +++ b/src/com/android/launcher3/allapps/search/LiveSearchManager.java @@ -15,8 +15,14 @@ */ 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; @@ -26,39 +32,49 @@ import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; +import android.util.Log; -import androidx.lifecycle.LiveData; +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; +import androidx.annotation.WorkerThread; +import androidx.lifecycle.Observer; import androidx.slice.Slice; -import androidx.slice.widget.SliceLiveData; +import androidx.slice.SliceViewManager; +import androidx.slice.SliceViewManager.SliceCallback; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.LauncherState; import com.android.launcher3.logging.InstanceId; -import com.android.launcher3.logging.InstanceIdSequence; +import com.android.launcher3.statemanager.StateManager.StateListener; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.widget.PendingAddWidgetInfo; +import java.util.ArrayList; import java.util.HashMap; import java.util.Optional; /** * Manages Lifecycle for Live search results */ -public class LiveSearchManager { +public class LiveSearchManager implements StateListener { + + private static final String TAG = "LiveSearchManager"; public static final int SEARCH_APPWIDGET_HOST_ID = 2048; private final Launcher mLauncher; - private final AppWidgetManager mAppWidgetManger; + private final HashMap mUriSliceMap = new HashMap<>(); + private final HashMap mWidgetPlaceholders = new HashMap<>(); - private final HashMap> mUriSliceMap = new HashMap<>(); private SearchWidgetHost mSearchWidgetHost; private InstanceId mLogInstanceId; public LiveSearchManager(Launcher launcher) { mLauncher = launcher; - mAppWidgetManger = AppWidgetManager.getInstance(launcher); + mLauncher.getStateManager().addStateListener(this); } /** @@ -67,78 +83,80 @@ public class LiveSearchManager { */ public SearchWidgetInfoContainer getPlaceHolderWidget(AppWidgetProviderInfo providerInfo) { if (mSearchWidgetHost == null) { - throw new RuntimeException("AppWidgetHost has not been created yet"); + mSearchWidgetHost = new SearchWidgetHost(mLauncher); + mSearchWidgetHost.startListening(); } ComponentName provider = providerInfo.provider; UserHandle userHandle = providerInfo.getProfile(); ComponentKey key = new ComponentKey(provider, userHandle); - SearchWidgetInfoContainer view = mWidgetPlaceholders.getOrDefault(key, null); 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 = mAppWidgetManger.bindAppWidgetIdIfAllowed(appWidgetId, userHandle, - provider, options); + boolean success = AppWidgetManager.getInstance(mLauncher) + .bindAppWidgetIdIfAllowed(appWidgetId, userHandle, provider, options); if (!success) { + mSearchWidgetHost.deleteAppWidgetId(appWidgetId); mWidgetPlaceholders.put(key, null); return null; } - view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView(mLauncher, appWidgetId, - providerInfo); + SearchWidgetInfoContainer view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView( + mLauncher, appWidgetId, providerInfo); view.setTag(pendingAddWidgetInfo); mWidgetPlaceholders.put(key, view); return view; } - /** - * Creates {@link LiveData} from Slice Uri. Caches created live data to be reused - * within the same search session. Removes previous observers when new SliceView request a - * live data for observation. - */ - public LiveData getSliceForUri(Uri sliceUri) { - LiveData sliceLiveData = mUriSliceMap.getOrDefault(sliceUri, null); - if (sliceLiveData == null) { - sliceLiveData = SliceLiveData.fromUri(mLauncher, sliceUri); - mUriSliceMap.put(sliceUri, sliceLiveData); - } - return sliceLiveData; - } - - /** - * Start search session - */ - public void start() { - stop(); - mLogInstanceId = new InstanceIdSequence().newInstanceId(); - mSearchWidgetHost = new SearchWidgetHost(mLauncher); - mSearchWidgetHost.startListening(); - } - /** * Stop search session */ public void stop() { + clearWidgetHost(); + } + + private void clearWidgetHost() { if (mSearchWidgetHost != null) { mSearchWidgetHost.stopListening(); + mSearchWidgetHost.clearViews(); mSearchWidgetHost.deleteHost(); - for (SearchWidgetInfoContainer placeholder : mWidgetPlaceholders.values()) { - placeholder.clearListeners(); - } mWidgetPlaceholders.clear(); mSearchWidgetHost = null; } - for (LiveData liveData : mUriSliceMap.values()) { - liveData.removeObservers(mLauncher); + } + + @Override + public void onStateTransitionComplete(LauncherState finalState) { + if (finalState != ALL_APPS) { + // Clear all search session related objects + mUriSliceMap.values().forEach(SliceLifeCycle::destroy); + mUriSliceMap.clear(); + + clearWidgetHost(); } - mUriSliceMap.clear(); + } + + /** + * Adds a new observer for the provided uri and returns a callback to cancel this observer + */ + public SafeCloseable addObserver(Uri uri, Observer listener) { + SliceLifeCycle slc = mUriSliceMap.get(uri); + if (slc == null) { + slc = new SliceLifeCycle(uri, mLauncher); + mUriSliceMap.put(uri, slc); + } + slc.addListener(listener); + + final SliceLifeCycle sliceLifeCycle = slc; + return () -> sliceLifeCycle.removeListener(listener); } /** @@ -159,5 +177,121 @@ public class LiveSearchManager { 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; + + 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/allapps/search/SearchWidgetInfoContainer.java b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java index b5c22688e5..8e5f8cb9c0 100644 --- a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java +++ b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java @@ -70,10 +70,4 @@ public class SearchWidgetInfoContainer extends AppWidgetHostView { mListeners.remove(hostView); } - /** - * Removes all AppWidgetHost update listeners - */ - public void clearListeners() { - mListeners.clear(); - } } diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java index 80135571b2..61f2c2adae 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -31,7 +31,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ShortcutInfo; import android.graphics.drawable.Drawable; -import android.os.Handler; import android.os.Process; import android.os.UserHandle; import android.util.Log; @@ -134,7 +133,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 IconLoadRequest updateIconInBackground(final ItemInfoUpdateReceiver caller, + public HandlerRunnable updateIconInBackground(final ItemInfoUpdateReceiver caller, final ItemInfoWithIcon info) { Preconditions.assertUIThread(); if (mPendingIconRequestCount <= 0) { @@ -142,20 +141,18 @@ public class IconCache extends BaseIconCache { } mPendingIconRequestCount ++; - IconLoadRequest request = new IconLoadRequest(mWorkerHandler, this::onIconRequestEnd) { - @Override - public void run() { - if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) { - getTitleAndIcon(info, false); - } else if (info instanceof PackageItemInfo) { - getTitleAndIconForApp((PackageItemInfo) info, false); - } - MAIN_EXECUTOR.execute(() -> { - caller.reapplyItemInfo(info); - onEnd(); - }); - } - }; + HandlerRunnable request = new HandlerRunnable<>(mWorkerHandler, + () -> { + if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) { + getTitleAndIcon(info, false); + } else if (info instanceof PackageItemInfo) { + getTitleAndIconForApp((PackageItemInfo) info, false); + } + return info; + }, + MAIN_EXECUTOR, + caller::reapplyItemInfo, + this::onIconRequestEnd); Utilities.postAsyncCallback(mWorkerHandler, request); return request; } @@ -336,12 +333,6 @@ public class IconCache extends BaseIconCache { return super.getEntryFromDB(cacheKey, entry, lowRes); } - public static abstract class IconLoadRequest extends HandlerRunnable { - IconLoadRequest(Handler handler, Runnable endRunnable) { - super(handler, endRunnable); - } - } - /** * Interface for receiving itemInfo with high-res icon. */