From efb665d3ec3d1e2e0afff4fca16924cca9b27fb7 Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Wed, 30 Oct 2019 13:24:14 -0700 Subject: [PATCH] Implement hotseat prediction client Bug: 142753423 Test: Manual Change-Id: I9f697c474b29340c9b33ddf896a49e8f3f893f8b --- .../HotseatPredictionController.java | 201 +++++++++++++----- .../appprediction/DynamicItemCache.java | 36 ++++ .../PredictionUiStateManager.java | 44 +--- .../uioverrides/QuickstepLauncher.java | 27 ++- src/com/android/launcher3/BubbleTextView.java | 3 +- 5 files changed, 207 insertions(+), 104 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java index ba44213a67..32846dc806 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/HotseatPredictionController.java @@ -15,7 +15,15 @@ */ package com.android.launcher3; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; + import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.app.prediction.AppPredictionContext; +import android.app.prediction.AppPredictionManager; +import android.app.prediction.AppPredictor; +import android.app.prediction.AppTarget; +import android.content.ComponentName; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -25,14 +33,16 @@ import androidx.annotation.Nullable; import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.appprediction.ComponentKeyMapper; -import com.android.launcher3.appprediction.PredictionUiStateManager; -import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.appprediction.DynamicItemCache; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.SystemShortcut; +import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.uioverrides.QuickstepLauncher; +import com.android.launcher3.util.ComponentKey; import java.util.ArrayList; import java.util.Collections; @@ -43,73 +53,86 @@ import java.util.List; * pinning of predicted apps and manages replacement of predicted apps with user drag. */ public class HotseatPredictionController implements DragController.DragListener, - View.OnAttachStateChangeListener, SystemShortcut.Factory { + View.OnAttachStateChangeListener, SystemShortcut.Factory, + InvariantDeviceProfile.OnIDPChangeListener, AllAppsStore.OnUpdateListener, + IconCache.ItemInfoUpdateReceiver { private static final String TAG = "PredictiveHotseat"; private static final boolean DEBUG = false; + private static final String PREDICTION_CLIENT = "hotseat"; private boolean mDragStarted = false; - private PredictionUiStateManager mPredictionUiStateManager; - private ArrayList mPredictedApps = new ArrayList<>(); + private int mHotSeatItemsCount; + private Launcher mLauncher; + private Hotseat mHotseat; + + private List mComponentKeyMappers = new ArrayList<>(); + + private DynamicItemCache mDynamicItemCache; + + private AppPredictor mAppPredictor; + private AllAppsStore mAllAppsStore; public HotseatPredictionController(Launcher launcher) { mLauncher = launcher; - mPredictionUiStateManager = PredictionUiStateManager.INSTANCE.get(mLauncher); - if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) { - mLauncher.getHotseat().addOnAttachStateChangeListener(this); - } + mHotseat = launcher.getHotseat(); + mAllAppsStore = mLauncher.getAppsView().getAppsStore(); + mAllAppsStore.addUpdateListener(this); + mDynamicItemCache = new DynamicItemCache(mLauncher, () -> fillGapsWithPrediction(false)); + mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons; + launcher.getDeviceProfile().inv.addOnChangeListener(this); + mHotseat.addOnAttachStateChangeListener(this); + createPredictor(); } @Override public void onViewAttachedToWindow(View view) { - mPredictionUiStateManager.setHotseatPredictionController(this); mLauncher.getDragController().addDragListener(this); } @Override public void onViewDetachedFromWindow(View view) { - mPredictionUiStateManager.setHotseatPredictionController(null); mLauncher.getDragController().removeDragListener(this); } - /** - * sets the list of predicted items. gets called from PredictionUiStateManager - */ - public void setPredictedApps(List apps) { - mPredictedApps.clear(); - mPredictedApps.addAll(mapToWorkspaceItemInfo(apps)); - fillGapsWithPrediction(false); - } - /** * Fills gaps in the hotseat with predictions */ public void fillGapsWithPrediction(boolean animate) { - if (!FeatureFlags.ENABLE_HYBRID_HOTSEAT.get() || mDragStarted) { + if (mDragStarted) { return; } - removePredictedApps(false); + List predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers); int predictionIndex = 0; - ArrayList itemInfos = new ArrayList<>(); - int cellCount = mLauncher.getWallpaperDeviceProfile().inv.numHotseatIcons; - for (int rank = 0; rank < cellCount; rank++) { - if (mPredictedApps.size() == predictionIndex) { - break; - } - View child = mLauncher.getHotseat().getChildAt( - mLauncher.getHotseat().getCellXFromOrder(rank), - mLauncher.getHotseat().getCellYFromOrder(rank)); - if (child != null) { - // we already have an item there. skip cell + ArrayList newItemsToAdd = new ArrayList<>(); + for (int rank = 0; rank < mHotSeatItemsCount; rank++) { + View child = mHotseat.getChildAt( + mHotseat.getCellXFromOrder(rank), + mHotseat.getCellYFromOrder(rank)); + + if (child != null && !isPredictedIcon(child)) { continue; } - WorkspaceItemInfo predictedItem = mPredictedApps.get(predictionIndex++); + if (predictedApps.size() <= predictionIndex) { + // Remove predicted apps from the past + if (isPredictedIcon(child)) { + mHotseat.removeView(child); + } + continue; + } + + WorkspaceItemInfo predictedItem = predictedApps.get(predictionIndex++); + if (isPredictedIcon(child)) { + BubbleTextView icon = (BubbleTextView) child; + icon.applyFromWorkspaceItem(predictedItem); + } else { + newItemsToAdd.add(predictedItem); + } preparePredictionInfo(predictedItem, rank); - itemInfos.add(predictedItem); } - mLauncher.bindItems(itemInfos, animate); + mLauncher.bindItems(newItemsToAdd, animate); for (BubbleTextView icon : getPredictedIcons()) { icon.verifyHighRes(); icon.setOnLongClickListener((v) -> { @@ -119,29 +142,73 @@ public class HotseatPredictionController implements DragController.DragListener, } } + /** + * Unregisters callbacks and frees resources + */ + public void destroy() { + mAllAppsStore.removeUpdateListener(this); + mLauncher.getDeviceProfile().inv.removeOnChangeListener(this); + mHotseat.removeOnAttachStateChangeListener(this); + if (mAppPredictor != null) { + mAppPredictor.destroy(); + } + } + + private void createPredictor() { + AppPredictionManager apm = mLauncher.getSystemService(AppPredictionManager.class); + if (apm == null) { + return; + } + if (mAppPredictor != null) { + mAppPredictor.destroy(); + } + mAppPredictor = apm.createAppPredictionSession( + new AppPredictionContext.Builder(mLauncher) + .setUiSurface(PREDICTION_CLIENT) + .setPredictedTargetCount(mHotSeatItemsCount) + .build()); + mAppPredictor.registerPredictionUpdates(mLauncher.getMainExecutor(), + this::setPredictedApps); + mAppPredictor.requestPredictionUpdate(); + } + + private void setPredictedApps(List appTargets) { + mComponentKeyMappers.clear(); + for (AppTarget appTarget : appTargets) { + ComponentKey key; + if (appTarget.getShortcutInfo() != null) { + key = ShortcutKey.fromInfo(appTarget.getShortcutInfo()); + } else { + key = new ComponentKey(new ComponentName(appTarget.getPackageName(), + appTarget.getClassName()), appTarget.getUser()); + } + mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache)); + } + updateDependencies(); + fillGapsWithPrediction(false); + } + + private void updateDependencies() { + mDynamicItemCache.updateDependencies(mComponentKeyMappers, mAllAppsStore, this, + mHotSeatItemsCount); + } + private void pinPrediction(ItemInfo info) { - BubbleTextView icon = (BubbleTextView) mLauncher.getHotseat().getChildAt( - mLauncher.getHotseat().getCellXFromOrder(info.rank), - mLauncher.getHotseat().getCellYFromOrder(info.rank)); + BubbleTextView icon = (BubbleTextView) mHotseat.getChildAt( + mHotseat.getCellXFromOrder(info.rank), + mHotseat.getCellYFromOrder(info.rank)); if (icon == null) { return; } WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo((WorkspaceItemInfo) info); - workspaceItemInfo.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT; mLauncher.getModelWriter().addItemToDatabase(workspaceItemInfo, - workspaceItemInfo.container, workspaceItemInfo.screenId, + LauncherSettings.Favorites.CONTAINER_HOTSEAT, workspaceItemInfo.screenId, workspaceItemInfo.cellX, workspaceItemInfo.cellY); - icon.animate().scaleY(0.8f).scaleX(0.8f).setListener(new AnimationSuccessListener() { - @Override - public void onAnimationSuccess(Animator animator) { - icon.animate().scaleY(1).scaleX(1); - } - }); + ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 1, 0.8f, 1).start(); icon.applyFromWorkspaceItem(workspaceItemInfo); icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE); } - private List mapToWorkspaceItemInfo( List components) { AllAppsStore allAppsStore = mLauncher.getAppsView().getAppsStore(); @@ -163,7 +230,7 @@ public class HotseatPredictionController implements DragController.DragListener, } } // Stop at the number of hotseat items - if (predictedApps.size() == mLauncher.getDeviceProfile().inv.numHotseatIcons) { + if (predictedApps.size() == mHotSeatItemsCount) { break; } } @@ -172,12 +239,10 @@ public class HotseatPredictionController implements DragController.DragListener, private List getPredictedIcons() { List icons = new ArrayList<>(); - ViewGroup vg = mLauncher.getHotseat().getShortcutsAndWidgets(); + ViewGroup vg = mHotseat.getShortcutsAndWidgets(); for (int i = 0; i < vg.getChildCount(); i++) { View child = vg.getChildAt(i); - if (child instanceof BubbleTextView && child.getTag() instanceof WorkspaceItemInfo - && ((WorkspaceItemInfo) child.getTag()).container - == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) { + if (isPredictedIcon(child)) { icons.add((BubbleTextView) child); } } @@ -191,13 +256,13 @@ public class HotseatPredictionController implements DragController.DragListener, @Override public void onAnimationSuccess(Animator animator) { if (icon.getParent() != null) { - ((ViewGroup) icon.getParent()).removeView(icon); + mHotseat.removeView(icon); } } }); } else { if (icon.getParent() != null) { - ((ViewGroup) icon.getParent()).removeView(icon); + mHotseat.removeView(icon); } } } @@ -222,7 +287,6 @@ public class HotseatPredictionController implements DragController.DragListener, @Override public SystemShortcut getShortcut(QuickstepLauncher activity, ItemInfo itemInfo) { - if (!FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) return null; if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) { return null; } @@ -233,10 +297,27 @@ public class HotseatPredictionController implements DragController.DragListener, itemInfo.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; itemInfo.rank = rank; itemInfo.cellX = rank; - itemInfo.cellY = LauncherAppState.getIDP(mLauncher).numHotseatIcons - rank - 1; + itemInfo.cellY = mHotSeatItemsCount - rank - 1; itemInfo.screenId = rank; } + @Override + public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) { + this.mHotSeatItemsCount = profile.numHotseatIcons; + createPredictor(); + } + + @Override + public void onAppsUpdated() { + updateDependencies(); + fillGapsWithPrediction(false); + } + + @Override + public void reapplyItemInfo(ItemInfoWithIcon info) { + + } + private class PinPrediction extends SystemShortcut { private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) { @@ -250,4 +331,10 @@ public class HotseatPredictionController implements DragController.DragListener, pinPrediction(mItemInfo); } } + + private static boolean isPredictedIcon(View view) { + return view instanceof BubbleTextView && view.getTag() instanceof WorkspaceItemInfo + && ((WorkspaceItemInfo) view.getTag()).container + == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java index 06580b95a3..38bb180ecb 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java @@ -18,6 +18,7 @@ package com.android.launcher3.appprediction; import static android.content.pm.PackageManager.MATCH_INSTANT; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER; import android.content.Context; import android.content.Intent; @@ -37,8 +38,10 @@ import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; +import com.android.launcher3.AppInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.WorkspaceItemInfo; +import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.shortcuts.DeepShortcutManager; @@ -72,6 +75,7 @@ public class DynamicItemCache { private final Handler mUiHandler; private final InstantAppResolver mInstantAppResolver; private final Runnable mOnUpdateCallback; + private final IconCache mIconCache; private final Map mShortcuts; private final Map mInstantApps; @@ -82,6 +86,7 @@ public class DynamicItemCache { mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage); mInstantAppResolver = InstantAppResolver.newInstance(context); mOnUpdateCallback = onUpdateCallback; + mIconCache = LauncherAppState.getInstance(mContext).getIconCache(); mShortcuts = new HashMap<>(); mInstantApps = new HashMap<>(); @@ -240,4 +245,35 @@ public class DynamicItemCache { public WorkspaceItemInfo getShortcutInfo(ShortcutKey key) { return mShortcuts.get(key); } + + /** + * requests and caches icons for app targets + */ + public void updateDependencies(List componentKeyMappers, + AllAppsStore appsStore, IconCache.ItemInfoUpdateReceiver callback, int itemCount) { + List instantAppsToLoad = new ArrayList<>(); + List shortcutsToLoad = new ArrayList<>(); + int total = componentKeyMappers.size(); + for (int i = 0, count = 0; i < total && count < itemCount; i++) { + ComponentKeyMapper mapper = componentKeyMappers.get(i); + // Update instant apps + if (COMPONENT_CLASS_MARKER.equals(mapper.getComponentClass())) { + instantAppsToLoad.add(mapper.getPackage()); + count++; + } else if (mapper.getComponentKey() instanceof ShortcutKey) { + shortcutsToLoad.add((ShortcutKey) mapper.getComponentKey()); + count++; + } else { + // Reload high res icon + AppInfo info = (AppInfo) mapper.getApp(appsStore); + if (info != null) { + if (info.usingLowResIcon()) { + mIconCache.updateIconInBackground(callback, info); + } + count++; + } + } + } + cacheItems(shortcutsToLoad, instantAppsToLoad); + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java index 8fade5cd38..8338c2e437 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,15 +18,12 @@ package com.android.launcher3.appprediction; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER; import android.app.prediction.AppPredictor; import android.app.prediction.AppTarget; import android.content.ComponentName; import android.content.Context; -import com.android.launcher3.AppInfo; -import com.android.launcher3.HotseatPredictionController; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener; import com.android.launcher3.ItemInfoWithIcon; @@ -37,7 +34,6 @@ import com.android.launcher3.LauncherStateManager.StateListener; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener; -import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.util.ComponentKey; @@ -90,8 +86,6 @@ public class PredictionUiStateManager implements StateListener, ItemInfoUpdateRe private AllAppsContainerView mAppsView; - private HotseatPredictionController mHotseatPredictionController; - private PredictionState mPendingState; private PredictionState mCurrentState; @@ -153,10 +147,6 @@ public class PredictionUiStateManager implements StateListener, ItemInfoUpdateRe updateDependencies(mCurrentState); } - public void setHotseatPredictionController(HotseatPredictionController controller) { - mHotseatPredictionController = controller; - } - @Override public void reapplyItemInfo(ItemInfoWithIcon info) { } @@ -193,9 +183,6 @@ public class PredictionUiStateManager implements StateListener, ItemInfoUpdateRe mAppsView.getFloatingHeaderView().findFixedRowByType(PredictionRowView.class) .setPredictedApps(mCurrentState.apps); } - if (mHotseatPredictionController != null) { - mHotseatPredictionController.setPredictedApps(mCurrentState.apps); - } } private void updatePredictionStateAfterCallback() { @@ -260,33 +247,8 @@ public class PredictionUiStateManager implements StateListener, ItemInfoUpdateRe if (!state.isEnabled || mAppsView == null) { return; } - - IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache(); - List instantAppsToLoad = new ArrayList<>(); - List shortcutsToLoad = new ArrayList<>(); - int total = state.apps.size(); - for (int i = 0, count = 0; i < total && count < mMaxIconsPerRow; i++) { - ComponentKeyMapper mapper = state.apps.get(i); - // Update instant apps - if (COMPONENT_CLASS_MARKER.equals(mapper.getComponentClass())) { - instantAppsToLoad.add(mapper.getPackage()); - count++; - } else if (mapper.getComponentKey() instanceof ShortcutKey) { - shortcutsToLoad.add((ShortcutKey) mapper.getComponentKey()); - count++; - } else { - // Reload high res icon - AppInfo info = (AppInfo) mapper.getApp(mAppsView.getAppsStore()); - if (info != null) { - if (info.usingLowResIcon()) { - // TODO: Update icon cache to support null callbacks. - iconCache.updateIconInBackground(this, info); - } - count++; - } - } - } - mDynamicItemCache.cacheItems(shortcutsToLoad, instantAppsToLoad); + mDynamicItemCache.updateDependencies(state.apps, mAppsView.getAppsStore(), this, + mMaxIconsPerRow); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index a6fcf440e2..eefb7dc039 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,6 +31,7 @@ import com.android.launcher3.HotseatPredictionController; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController; @@ -137,7 +138,9 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mHotseatPredictionController = new HotseatPredictionController(this); + if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) { + mHotseatPredictionController = new HotseatPredictionController(this); + } } @Override @@ -165,8 +168,12 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { @Override public Stream getSupportedShortcuts() { - return Stream.concat(super.getSupportedShortcuts(), - Stream.of(mHotseatPredictionController)); + if (mHotseatPredictionController != null) { + return Stream.concat(super.getSupportedShortcuts(), + Stream.of(mHotseatPredictionController)); + } else { + return super.getSupportedShortcuts(); + } } /** @@ -187,7 +194,17 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { @Override public void finishBindingItems(int pageBoundFirst) { super.finishBindingItems(pageBoundFirst); - mHotseatPredictionController.fillGapsWithPrediction(false); + if (mHotseatPredictionController != null) { + mHotseatPredictionController.fillGapsWithPrediction(false); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (mHotseatPredictionController != null) { + mHotseatPredictionController.destroy(); + } } @Override diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index ff5f33d526..01e9b6e606 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -496,7 +496,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, // Text should be visible everywhere but the hotseat. Object tag = getParent() instanceof FolderIcon ? ((View) getParent()).getTag() : getTag(); ItemInfo info = tag instanceof ItemInfo ? (ItemInfo) tag : null; - return info == null || info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT; + return info == null || (info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT + && info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION); } public void setTextVisibility(boolean visible) {