diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 6302739bc5..74ec7ee327 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -96,6 +96,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private static final int MAX_SEARCH_LOOP_COUNT = 20; private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed}; + private static final float HIGHLIGHT_SCALE = 1.16f; private final PointF mTranslationForReorderBounce = new PointF(0, 0); private final PointF mTranslationForReorderPreview = new PointF(0, 0); @@ -258,12 +259,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, mDotParams.scale = 0f; mForceHideDot = false; setBackground(null); - - setTag(null); - if (mIconLoadRequest != null) { - mIconLoadRequest.cancel(); - mIconLoadRequest = null; - } } private void cancelDotScaleAnim() { @@ -368,7 +363,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } } - public void setBubbleTextHolder(BubbleTextHolder bubbleTextHolder) { + public void setBubbleTextHolder( + BubbleTextHolder bubbleTextHolder) { mBubbleTextHolder = bubbleTextHolder; } @@ -1024,6 +1020,19 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, getIconBounds(mIconSize, bounds); } + private int getIconSizeForDisplay(int display) { + DeviceProfile grid = mActivity.getDeviceProfile(); + switch (display) { + case DISPLAY_ALL_APPS: + return grid.allAppsIconSizePx; + case DISPLAY_FOLDER: + return grid.folderChildIconSizePx; + case DISPLAY_WORKSPACE: + default: + return grid.iconSizePx; + } + } + public void getSourceVisualDragBounds(Rect bounds) { getIconBounds(mIconSize, bounds); } @@ -1036,8 +1045,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } private void resetIconScale() { - if (mIcon != null) { - mIcon.resetScale(); + if (mIcon instanceof FastBitmapDrawable) { + ((FastBitmapDrawable) mIcon).resetScale(); } } diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 00156b1f8d..7687fea85d 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -18,9 +18,6 @@ package com.android.launcher3.allapps; import android.content.Context; -import androidx.recyclerview.widget.DiffUtil; -import androidx.recyclerview.widget.DiffUtil.DiffResult; - import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; @@ -46,6 +43,10 @@ public class AlphabeticalAppsList implement public static final String TAG = "AlphabeticalAppsList"; + private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION = 0; + private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS = 1; + + private final int mFastScrollDistributionMode = FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS; private final WorkAdapterProvider mWorkAdapterProvider; /** @@ -197,11 +198,8 @@ public class AlphabeticalAppsList implement public boolean appendSearchResults(ArrayList results) { if (hasFilter() && results != null && results.size() > 0) { - int pos = mSearchResults.size(); - updateSearchAdapterItems(results, pos); - if (mAdapter != null) { - mAdapter.notifyItemRangeInserted(pos, results.size()); - } + updateSearchAdapterItems(results, mSearchResults.size()); + refreshRecyclerView(); return true; } return false; @@ -275,6 +273,10 @@ public class AlphabeticalAppsList implement */ public void updateAdapterItems() { refillAdapterItems(); + refreshRecyclerView(); + } + + private void refreshRecyclerView() { if (mAdapter != null) { mAdapter.notifyDataSetChanged(); } @@ -284,9 +286,9 @@ public class AlphabeticalAppsList implement String lastSectionName = null; FastScrollSectionInfo lastFastScrollerSectionInfo = null; int position = 0; + int appIndex = 0; // Prepare to update the list of sections, filtered apps, etc. - ArrayList oldList = new ArrayList<>(mAdapterItems); mAccessibilityResultsCount = 0; mFastScrollerSections.clear(); mAdapterItems.clear(); @@ -313,7 +315,8 @@ public class AlphabeticalAppsList implement } // Create an app item - AdapterItem appItem = AdapterItem.asApp(position++, info); + AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info, + appIndex++); if (lastFastScrollerSectionInfo.fastScrollToItem == null) { lastFastScrollerSectionInfo.fastScrollToItem = appItem; } @@ -339,7 +342,6 @@ public class AlphabeticalAppsList implement int numAppsInSection = 0; int numAppsInRow = 0; int rowIndex = -1; - for (AdapterItem item : mAdapterItems) { item.rowIndex = 0; if (BaseAllAppsAdapter.isDividerViewType(item.viewType)) { @@ -358,50 +360,35 @@ public class AlphabeticalAppsList implement mNumAppRowsInAdapter = rowIndex + 1; // Pre-calculate all the fast scroller fractions - float perSectionTouchFraction = 1f / mFastScrollerSections.size(); - float cumulativeTouchFraction = 0f; - for (FastScrollSectionInfo info : mFastScrollerSections) { - AdapterItem item = info.fastScrollToItem; - if (!BaseAllAppsAdapter.isIconViewType(item.viewType)) { - info.touchFraction = 0f; - continue; - } - info.touchFraction = cumulativeTouchFraction; - cumulativeTouchFraction += perSectionTouchFraction; + switch (mFastScrollDistributionMode) { + case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION: + float rowFraction = 1f / mNumAppRowsInAdapter; + for (FastScrollSectionInfo info : mFastScrollerSections) { + AdapterItem item = info.fastScrollToItem; + if (!BaseAllAppsAdapter.isIconViewType(item.viewType)) { + info.touchFraction = 0f; + continue; + } + + float subRowFraction = + item.rowAppIndex * (rowFraction / mNumAppsPerRowAllApps); + info.touchFraction = item.rowIndex * rowFraction + subRowFraction; + } + break; + case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS: + float perSectionTouchFraction = 1f / mFastScrollerSections.size(); + float cumulativeTouchFraction = 0f; + for (FastScrollSectionInfo info : mFastScrollerSections) { + AdapterItem item = info.fastScrollToItem; + if (!BaseAllAppsAdapter.isIconViewType(item.viewType)) { + info.touchFraction = 0f; + continue; + } + info.touchFraction = cumulativeTouchFraction; + cumulativeTouchFraction += perSectionTouchFraction; + } + break; } } - - DiffResult result = DiffUtil.calculateDiff(new DiffCallback(oldList, mAdapterItems)); - } - - private static class DiffCallback extends DiffUtil.Callback { - private final List mOldItems; - private final List mNewItems; - - DiffCallback(List oldItems, List newItems) { - mOldItems = oldItems; - mNewItems = newItems; - } - - @Override - public int getOldListSize() { - return mOldItems.size(); - } - - @Override - public int getNewListSize() { - return mNewItems.size(); - } - - @Override - public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { - return mOldItems.get(oldItemPosition).getStableId() - == mNewItems.get(newItemPosition).getStableId(); - } - - @Override - public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { - return mOldItems.get(oldItemPosition).isContentSame(mNewItems.get(newItemPosition)); - } } } diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java index e912cf22da..976284d947 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java @@ -36,6 +36,7 @@ import com.android.launcher3.BubbleTextView; import com.android.launcher3.R; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.views.ActivityContext; import java.util.Arrays; @@ -93,21 +94,31 @@ public abstract class BaseAllAppsAdapter ex // The type of this item public int viewType; + // The section name of this item. Note that there can be multiple items with different + // sectionNames in the same section + public String sectionName = null; // The row that this item shows up on public int rowIndex; // The index of this app in the row public int rowAppIndex; // The associated ItemInfoWithIcon for the item - public AppInfo itemInfo = null; + public ItemInfoWithIcon itemInfo = null; + // The index of this app not including sections + public int appIndex = -1; + // Search section associated to result + public DecorationInfo decorationInfo = null; /** * Factory method for AppIcon AdapterItem */ - public static AdapterItem asApp(int pos, AppInfo appInfo) { + public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo, + int appIndex) { AdapterItem item = new AdapterItem(); item.viewType = VIEW_TYPE_ICON; item.position = pos; + item.sectionName = sectionName; item.itemInfo = appInfo; + item.appIndex = appIndex; return item; } @@ -144,23 +155,6 @@ public abstract class BaseAllAppsAdapter ex protected boolean isCountedForAccessibility() { return viewType == VIEW_TYPE_ICON || viewType == VIEW_TYPE_SEARCH_MARKET; } - - public long getStableId() { - return viewType; - } - - /** - * Called to check if the content of the item is same as the other item. This is called only - * if the {@link #getStableId()} matches for both the items. - */ - public boolean isContentSame(AdapterItem other) { - // We can use itemInfo for diff, but since ItemInfo objects are singleton per Model, - // this could prevent updates within this itemInfo object itself (like title change - // or flag changes). We can create a better diffing logic if we store a clone a snapshot - // of the itemInfo, but that would cause icons to be loaded lazily on the cloned object - // instead of the singleton object. - return false; - } } protected final T mActivityContext; @@ -273,7 +267,11 @@ public abstract class BaseAllAppsAdapter ex AdapterItem adapterItem = mApps.getAdapterItems().get(position); BubbleTextView icon = (BubbleTextView) holder.itemView; icon.reset(); - icon.applyFromApplicationInfo(adapterItem.itemInfo); + if (adapterItem.itemInfo instanceof AppInfo) { + icon.applyFromApplicationInfo((AppInfo) adapterItem.itemInfo); + } else { + icon.applyFromItemInfoWithIcon(adapterItem.itemInfo); + } break; case VIEW_TYPE_EMPTY_SEARCH: TextView emptyViewText = (TextView) holder.itemView; diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java index 31c0c69355..f913aa9093 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java @@ -45,7 +45,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.graphics.ColorUtils; import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile.DeviceProfileListenable; @@ -728,12 +727,6 @@ public abstract class BaseAllAppsContainerView { for (int i = 0; i < total && resultCount < MAX_RESULTS_COUNT; i++) { AppInfo info = apps.get(i); if (StringMatcherUtility.matches(queryTextLower, info.title.toString(), matcher)) { - AdapterItem appItem = AdapterItem.asApp(resultCount, info); + AdapterItem appItem = AdapterItem.asApp(resultCount, "", info, resultCount); result.add(appItem); resultCount++; } diff --git a/src/com/android/launcher3/util/Executors.java b/src/com/android/launcher3/util/Executors.java index 6978e0c2a4..8485371a79 100644 --- a/src/com/android/launcher3/util/Executors.java +++ b/src/com/android/launcher3/util/Executors.java @@ -15,12 +15,17 @@ */ package com.android.launcher3.util; +import static android.os.Process.THREAD_PRIORITY_BACKGROUND; + +import static java.util.concurrent.Executors.newSingleThreadExecutor; + import android.os.HandlerThread; import android.os.Looper; import android.os.Process; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; @@ -37,7 +42,7 @@ public class Executors { private static final int KEEP_ALIVE = 1; /** Dedicated executor instances for work depending on other packages. */ - private static final Map PACKAGE_EXECUTORS = new ConcurrentHashMap<>(); + private static final Map PACKAGE_EXECUTORS = new ConcurrentHashMap<>(); /** * An {@link ThreadPoolExecutor} to be used with async task with no limit on the queue size. @@ -85,10 +90,11 @@ public class Executors { * * @param packageName Package associated with the executor. */ - public static LooperExecutor getPackageExecutor(String packageName) { + public static ExecutorService getPackageExecutor(String packageName) { return PACKAGE_EXECUTORS.computeIfAbsent( - packageName, p -> new LooperExecutor( - createAndStartNewLooper(p, Process.THREAD_PRIORITY_DEFAULT))); + packageName, + p -> newSingleThreadExecutor( + new SimpleThreadFactory(p, THREAD_PRIORITY_BACKGROUND))); } /**