mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-17 09:38:19 +00:00
591 lines
23 KiB
Java
591 lines
23 KiB
Java
/*
|
|
* Copyright (C) 2015 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;
|
|
|
|
import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APPS;
|
|
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.res.Resources;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.view.Gravity;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.View.OnClickListener;
|
|
import android.view.View.OnFocusChangeListener;
|
|
import android.view.View.OnLongClickListener;
|
|
import android.view.ViewGroup;
|
|
import android.view.accessibility.AccessibilityEvent;
|
|
import android.widget.TextView;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.core.view.accessibility.AccessibilityEventCompat;
|
|
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
|
import androidx.core.view.accessibility.AccessibilityRecordCompat;
|
|
import androidx.lifecycle.Lifecycle;
|
|
import androidx.lifecycle.LifecycleOwner;
|
|
import androidx.lifecycle.LifecycleRegistry;
|
|
import androidx.lifecycle.LiveData;
|
|
import androidx.recyclerview.widget.GridLayoutManager;
|
|
import androidx.recyclerview.widget.RecyclerView;
|
|
import androidx.slice.Slice;
|
|
import androidx.slice.widget.SliceLiveData;
|
|
import androidx.slice.widget.SliceView;
|
|
|
|
import com.android.launcher3.BaseDraggingActivity;
|
|
import com.android.launcher3.BubbleTextView;
|
|
import com.android.launcher3.R;
|
|
import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler;
|
|
import com.android.launcher3.allapps.search.SearchSectionInfo;
|
|
import com.android.launcher3.config.FeatureFlags;
|
|
import com.android.launcher3.model.data.AppInfo;
|
|
import com.android.launcher3.util.PackageManagerHelper;
|
|
import com.android.launcher3.views.HeroSearchResultView;
|
|
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
|
import com.android.systemui.plugins.shared.SearchTarget;
|
|
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
|
|
|
import java.util.List;
|
|
import java.util.function.IntConsumer;
|
|
|
|
/**
|
|
* The grid view adapter of all the apps.
|
|
*/
|
|
public class AllAppsGridAdapter extends
|
|
RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> implements
|
|
LifecycleOwner {
|
|
|
|
public static final String TAG = "AppsGridAdapter";
|
|
|
|
// A normal icon
|
|
public static final int VIEW_TYPE_ICON = 1 << 1;
|
|
// The message shown when there are no filtered results
|
|
public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 2;
|
|
// The message to continue to a market search when there are no filtered results
|
|
public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 3;
|
|
|
|
// We use various dividers for various purposes. They share enough attributes to reuse layouts,
|
|
// but differ in enough attributes to require different view types
|
|
|
|
// A divider that separates the apps list and the search market button
|
|
public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4;
|
|
|
|
public static final int VIEW_TYPE_SEARCH_CORPUS_TITLE = 1 << 5;
|
|
|
|
public static final int VIEW_TYPE_SEARCH_HERO_APP = 1 << 6;
|
|
|
|
public static final int VIEW_TYPE_SEARCH_ROW_WITH_BUTTON = 1 << 7;
|
|
|
|
public static final int VIEW_TYPE_SEARCH_ROW = 1 << 8;
|
|
|
|
public static final int VIEW_TYPE_SEARCH_SLICE = 1 << 9;
|
|
|
|
public static final int VIEW_TYPE_SEARCH_SHORTCUT = 1 << 10;
|
|
|
|
public static final int VIEW_TYPE_SEARCH_PEOPLE = 1 << 11;
|
|
|
|
// Common view type masks
|
|
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
|
|
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
|
|
|
|
private final LifecycleRegistry mLifecycleRegistry;
|
|
|
|
/**
|
|
* ViewHolder for each icon.
|
|
*/
|
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
|
|
|
public ViewHolder(View v) {
|
|
super(v);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Info about a particular adapter item (can be either section or app)
|
|
*/
|
|
public static class AdapterItem {
|
|
/** Common properties */
|
|
// The index of this adapter item in the list
|
|
public int position;
|
|
// The type of this item
|
|
public int viewType;
|
|
|
|
/** App-only properties */
|
|
// The section name of this app. 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 AppInfo for the app
|
|
public AppInfo appInfo = null;
|
|
// The index of this app not including sections
|
|
public int appIndex = -1;
|
|
// Search section associated to result
|
|
public SearchSectionInfo searchSectionInfo = null;
|
|
|
|
/**
|
|
* Factory method for AppIcon AdapterItem
|
|
*/
|
|
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.appInfo = appInfo;
|
|
item.appIndex = appIndex;
|
|
return item;
|
|
}
|
|
|
|
/**
|
|
* Factory method for empty search results view
|
|
*/
|
|
public static AdapterItem asEmptySearch(int pos) {
|
|
AdapterItem item = new AdapterItem();
|
|
item.viewType = VIEW_TYPE_EMPTY_SEARCH;
|
|
item.position = pos;
|
|
return item;
|
|
}
|
|
|
|
/**
|
|
* Factory method for a dividerView in AllAppsSearch
|
|
*/
|
|
public static AdapterItem asAllAppsDivider(int pos) {
|
|
AdapterItem item = new AdapterItem();
|
|
item.viewType = VIEW_TYPE_ALL_APPS_DIVIDER;
|
|
item.position = pos;
|
|
return item;
|
|
}
|
|
|
|
/**
|
|
* Factory method for a market search button
|
|
*/
|
|
public static AdapterItem asMarketSearch(int pos) {
|
|
AdapterItem item = new AdapterItem();
|
|
item.viewType = VIEW_TYPE_SEARCH_MARKET;
|
|
item.position = pos;
|
|
return item;
|
|
}
|
|
|
|
boolean isCountedForAccessibility() {
|
|
return viewType == VIEW_TYPE_ICON
|
|
|| viewType == VIEW_TYPE_SEARCH_HERO_APP
|
|
|| viewType == VIEW_TYPE_SEARCH_ROW_WITH_BUTTON
|
|
|| viewType == VIEW_TYPE_SEARCH_SLICE
|
|
|| viewType == VIEW_TYPE_SEARCH_ROW
|
|
|| viewType == VIEW_TYPE_SEARCH_PEOPLE
|
|
|| viewType == VIEW_TYPE_SEARCH_SHORTCUT;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extension of AdapterItem that contains an extra payload specific to item
|
|
*
|
|
* @param <T> Play load Type
|
|
*/
|
|
public static class AdapterItemWithPayload<T> extends AdapterItem {
|
|
private T mPayload;
|
|
private AllAppsSearchPlugin mPlugin;
|
|
private IntConsumer mSelectionHandler;
|
|
|
|
public AllAppsSearchPlugin getPlugin() {
|
|
return mPlugin;
|
|
}
|
|
|
|
public void setPlugin(AllAppsSearchPlugin plugin) {
|
|
mPlugin = plugin;
|
|
}
|
|
|
|
public AdapterItemWithPayload(T payload, int type, AllAppsSearchPlugin plugin) {
|
|
mPayload = payload;
|
|
viewType = type;
|
|
mPlugin = plugin;
|
|
}
|
|
|
|
public void setSelectionHandler(IntConsumer runnable) {
|
|
mSelectionHandler = runnable;
|
|
}
|
|
|
|
public IntConsumer getSelectionHandler() {
|
|
return mSelectionHandler;
|
|
}
|
|
|
|
public T getPayload() {
|
|
return mPayload;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A subclass of GridLayoutManager that overrides accessibility values during app search.
|
|
*/
|
|
public class AppsGridLayoutManager extends GridLayoutManager {
|
|
|
|
public AppsGridLayoutManager(Context context) {
|
|
super(context, 1, GridLayoutManager.VERTICAL, false);
|
|
}
|
|
|
|
@Override
|
|
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
|
super.onInitializeAccessibilityEvent(event);
|
|
|
|
// Ensure that we only report the number apps for accessibility not including other
|
|
// adapter views
|
|
final AccessibilityRecordCompat record = AccessibilityEventCompat
|
|
.asRecord(event);
|
|
record.setItemCount(mApps.getNumFilteredApps());
|
|
record.setFromIndex(Math.max(0,
|
|
record.getFromIndex() - getRowsNotForAccessibility(record.getFromIndex())));
|
|
record.setToIndex(Math.max(0,
|
|
record.getToIndex() - getRowsNotForAccessibility(record.getToIndex())));
|
|
}
|
|
|
|
@Override
|
|
public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
|
|
RecyclerView.State state) {
|
|
return super.getRowCountForAccessibility(recycler, state) -
|
|
getRowsNotForAccessibility(mApps.getAdapterItems().size() - 1);
|
|
}
|
|
|
|
@Override
|
|
public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
|
|
RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
|
|
super.onInitializeAccessibilityNodeInfoForItem(recycler, state, host, info);
|
|
|
|
ViewGroup.LayoutParams lp = host.getLayoutParams();
|
|
AccessibilityNodeInfoCompat.CollectionItemInfoCompat cic = info.getCollectionItemInfo();
|
|
if (!(lp instanceof LayoutParams) || (cic == null)) {
|
|
return;
|
|
}
|
|
LayoutParams glp = (LayoutParams) lp;
|
|
info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
|
|
cic.getRowIndex() - getRowsNotForAccessibility(glp.getViewAdapterPosition()),
|
|
cic.getRowSpan(),
|
|
cic.getColumnIndex(),
|
|
cic.getColumnSpan(),
|
|
cic.isHeading(),
|
|
cic.isSelected()));
|
|
}
|
|
|
|
/**
|
|
* Returns the number of rows before {@param adapterPosition}, including this position
|
|
* which should not be counted towards the collection info.
|
|
*/
|
|
private int getRowsNotForAccessibility(int adapterPosition) {
|
|
List<AdapterItem> items = mApps.getAdapterItems();
|
|
adapterPosition = Math.max(adapterPosition, mApps.getAdapterItems().size() - 1);
|
|
int extraRows = 0;
|
|
for (int i = 0; i <= adapterPosition; i++) {
|
|
if (!isViewType(items.get(i).viewType, VIEW_TYPE_MASK_ICON)) {
|
|
extraRows++;
|
|
}
|
|
}
|
|
return extraRows;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper class to size the grid items.
|
|
*/
|
|
public class GridSpanSizer extends GridLayoutManager.SpanSizeLookup {
|
|
|
|
public GridSpanSizer() {
|
|
super();
|
|
setSpanIndexCacheEnabled(true);
|
|
}
|
|
|
|
@Override
|
|
public int getSpanSize(int position) {
|
|
if (isIconViewType(mApps.getAdapterItems().get(position).viewType)) {
|
|
return 1;
|
|
} else {
|
|
// Section breaks span the full width
|
|
return mAppsPerRow;
|
|
}
|
|
}
|
|
}
|
|
|
|
private final BaseDraggingActivity mLauncher;
|
|
private final LayoutInflater mLayoutInflater;
|
|
private final AlphabeticalAppsList mApps;
|
|
private final GridLayoutManager mGridLayoutMgr;
|
|
private final GridSpanSizer mGridSizer;
|
|
|
|
private final OnClickListener mOnIconClickListener;
|
|
private OnLongClickListener mOnIconLongClickListener = INSTANCE_ALL_APPS;
|
|
|
|
private int mAppsPerRow;
|
|
|
|
private OnFocusChangeListener mIconFocusListener;
|
|
|
|
// The text to show when there are no search results and no market search handler.
|
|
protected String mEmptySearchMessage;
|
|
// The intent to send off to the market app, updated each time the search query changes.
|
|
private Intent mMarketSearchIntent;
|
|
|
|
public AllAppsGridAdapter(BaseDraggingActivity launcher, LayoutInflater inflater,
|
|
AlphabeticalAppsList apps) {
|
|
Resources res = launcher.getResources();
|
|
mLauncher = launcher;
|
|
mApps = apps;
|
|
mEmptySearchMessage = res.getString(R.string.all_apps_loading_message);
|
|
mGridSizer = new GridSpanSizer();
|
|
mGridLayoutMgr = new AppsGridLayoutManager(launcher);
|
|
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
|
|
mLayoutInflater = inflater;
|
|
|
|
mOnIconClickListener = launcher.getItemOnClickListener();
|
|
|
|
setAppsPerRow(mLauncher.getDeviceProfile().inv.numAllAppsColumns);
|
|
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
|
mLifecycleRegistry = new LifecycleRegistry(this);
|
|
mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
|
|
} else {
|
|
mLifecycleRegistry = null;
|
|
}
|
|
}
|
|
|
|
public void setAppsPerRow(int appsPerRow) {
|
|
mAppsPerRow = appsPerRow;
|
|
mGridLayoutMgr.setSpanCount(mAppsPerRow);
|
|
}
|
|
|
|
/**
|
|
* Sets the long click listener for icons
|
|
*/
|
|
public void setOnIconLongClickListener(@Nullable OnLongClickListener listener) {
|
|
mOnIconLongClickListener = listener;
|
|
}
|
|
|
|
public static boolean isDividerViewType(int viewType) {
|
|
return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER);
|
|
}
|
|
|
|
public static boolean isIconViewType(int viewType) {
|
|
return isViewType(viewType, VIEW_TYPE_MASK_ICON);
|
|
}
|
|
|
|
public static boolean isViewType(int viewType, int viewTypeMask) {
|
|
return (viewType & viewTypeMask) != 0;
|
|
}
|
|
|
|
public void setIconFocusListener(OnFocusChangeListener focusListener) {
|
|
mIconFocusListener = focusListener;
|
|
}
|
|
|
|
/**
|
|
* Sets the last search query that was made, used to show when there are no results and to also
|
|
* seed the intent for searching the market.
|
|
*/
|
|
public void setLastSearchQuery(String query) {
|
|
Resources res = mLauncher.getResources();
|
|
mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
|
|
mMarketSearchIntent = PackageManagerHelper.getMarketSearchIntent(mLauncher, query);
|
|
}
|
|
|
|
/**
|
|
* Returns the grid layout manager.
|
|
*/
|
|
public GridLayoutManager getLayoutManager() {
|
|
return mGridLayoutMgr;
|
|
}
|
|
|
|
@Override
|
|
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
switch (viewType) {
|
|
case VIEW_TYPE_ICON:
|
|
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
|
|
R.layout.all_apps_icon, parent, false);
|
|
icon.setLongPressTimeoutFactor(1f);
|
|
icon.setOnFocusChangeListener(mIconFocusListener);
|
|
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
|
icon.setOnClickListener(mOnIconClickListener);
|
|
icon.setOnLongClickListener(mOnIconLongClickListener);
|
|
}
|
|
|
|
// Ensure the all apps icon height matches the workspace icons in portrait mode.
|
|
icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
|
|
return new ViewHolder(icon);
|
|
case VIEW_TYPE_EMPTY_SEARCH:
|
|
return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
|
|
parent, false));
|
|
case VIEW_TYPE_SEARCH_MARKET:
|
|
View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
|
|
parent, false);
|
|
searchMarketView.setOnClickListener(v -> mLauncher.startActivitySafely(
|
|
v, mMarketSearchIntent, null));
|
|
return new ViewHolder(searchMarketView);
|
|
case VIEW_TYPE_ALL_APPS_DIVIDER:
|
|
return new ViewHolder(mLayoutInflater.inflate(
|
|
R.layout.all_apps_divider, parent, false));
|
|
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
|
|
return new ViewHolder(
|
|
mLayoutInflater.inflate(R.layout.search_section_title, parent, false));
|
|
case VIEW_TYPE_SEARCH_HERO_APP:
|
|
return new ViewHolder(mLayoutInflater.inflate(
|
|
R.layout.search_result_hero_app, parent, false));
|
|
case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON:
|
|
return new ViewHolder(mLayoutInflater.inflate(
|
|
R.layout.search_result_play_item, parent, false));
|
|
case VIEW_TYPE_SEARCH_ROW:
|
|
return new ViewHolder(mLayoutInflater.inflate(
|
|
R.layout.search_result_settings_row, parent, false));
|
|
case VIEW_TYPE_SEARCH_SLICE:
|
|
return new ViewHolder(mLayoutInflater.inflate(
|
|
R.layout.search_result_slice, parent, false));
|
|
case VIEW_TYPE_SEARCH_SHORTCUT:
|
|
return new ViewHolder(mLayoutInflater.inflate(
|
|
R.layout.search_result_shortcut, parent, false));
|
|
case VIEW_TYPE_SEARCH_PEOPLE:
|
|
return new ViewHolder(mLayoutInflater.inflate(
|
|
R.layout.search_result_people_item, parent, false));
|
|
default:
|
|
throw new RuntimeException("Unexpected view type");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
|
switch (holder.getItemViewType()) {
|
|
case VIEW_TYPE_ICON:
|
|
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
|
|
AppInfo info = adapterItem.appInfo;
|
|
BubbleTextView icon = (BubbleTextView) holder.itemView;
|
|
icon.reset();
|
|
icon.applyFromApplicationInfo(info);
|
|
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
|
break;
|
|
}
|
|
//TODO: replace with custom TopHitBubbleTextView with support for both shortcut
|
|
// and apps
|
|
if (adapterItem instanceof AdapterItemWithPayload) {
|
|
AdapterItemWithPayload withPayload = (AdapterItemWithPayload) adapterItem;
|
|
IntConsumer selectionHandler = type -> {
|
|
SearchTargetEvent e = new SearchTargetEvent(SearchTarget.ItemType.APP,
|
|
type);
|
|
e.bundle = HeroSearchResultView.getAppBundle(info);
|
|
if (withPayload.getPlugin() != null) {
|
|
withPayload.getPlugin().notifySearchTargetEvent(e);
|
|
}
|
|
};
|
|
icon.setOnClickListener(view -> {
|
|
selectionHandler.accept(SearchTargetEvent.SELECT);
|
|
mOnIconClickListener.onClick(view);
|
|
});
|
|
icon.setOnLongClickListener(view -> {
|
|
selectionHandler.accept(SearchTargetEvent.LONG_PRESS);
|
|
return mOnIconLongClickListener.onLongClick(view);
|
|
});
|
|
withPayload.setSelectionHandler(selectionHandler);
|
|
}
|
|
break;
|
|
case VIEW_TYPE_EMPTY_SEARCH:
|
|
TextView emptyViewText = (TextView) holder.itemView;
|
|
emptyViewText.setText(mEmptySearchMessage);
|
|
emptyViewText.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER :
|
|
Gravity.START | Gravity.CENTER_VERTICAL);
|
|
break;
|
|
case VIEW_TYPE_SEARCH_MARKET:
|
|
TextView searchView = (TextView) holder.itemView;
|
|
if (mMarketSearchIntent != null) {
|
|
searchView.setVisibility(View.VISIBLE);
|
|
} else {
|
|
searchView.setVisibility(View.GONE);
|
|
}
|
|
break;
|
|
case VIEW_TYPE_SEARCH_SLICE:
|
|
SliceView sliceView = (SliceView) holder.itemView;
|
|
AdapterItemWithPayload<Uri> item =
|
|
(AdapterItemWithPayload<Uri>) mApps.getAdapterItems().get(position);
|
|
sliceView.setOnSliceActionListener((info1, s) -> {
|
|
if (item.getPlugin() != null) {
|
|
SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
|
|
SearchTarget.ItemType.SETTINGS_SLICE,
|
|
SearchTargetEvent.CHILD_SELECT);
|
|
searchTargetEvent.bundle = new Bundle();
|
|
searchTargetEvent.bundle.putParcelable("uri", item.getPayload());
|
|
item.getPlugin().notifySearchTargetEvent(searchTargetEvent);
|
|
}
|
|
});
|
|
try {
|
|
LiveData<Slice> liveData = SliceLiveData.fromUri(mLauncher, item.getPayload());
|
|
liveData.observe(this::getLifecycle, sliceView);
|
|
sliceView.setTag(liveData);
|
|
} catch (Exception ignored) {
|
|
}
|
|
break;
|
|
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
|
|
case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON:
|
|
case VIEW_TYPE_SEARCH_HERO_APP:
|
|
case VIEW_TYPE_SEARCH_ROW:
|
|
case VIEW_TYPE_SEARCH_SHORTCUT:
|
|
case VIEW_TYPE_SEARCH_PEOPLE:
|
|
PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView;
|
|
payloadResultView.applyAdapterInfo(
|
|
(AdapterItemWithPayload) mApps.getAdapterItems().get(position));
|
|
break;
|
|
case VIEW_TYPE_ALL_APPS_DIVIDER:
|
|
// nothing to do
|
|
break;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onViewRecycled(@NonNull ViewHolder holder) {
|
|
super.onViewRecycled(holder);
|
|
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
|
|
if (holder.itemView instanceof BubbleTextView) {
|
|
BubbleTextView icon = (BubbleTextView) holder.itemView;
|
|
icon.setOnClickListener(mOnIconClickListener);
|
|
icon.setOnLongClickListener(mOnIconLongClickListener);
|
|
} else if (holder.itemView instanceof SliceView) {
|
|
SliceView sliceView = (SliceView) holder.itemView;
|
|
sliceView.setOnSliceActionListener(null);
|
|
if (sliceView.getTag() instanceof LiveData) {
|
|
LiveData sliceLiveData = (LiveData) sliceView.getTag();
|
|
sliceLiveData.removeObservers(this::getLifecycle);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
public boolean onFailedToRecycleView(ViewHolder holder) {
|
|
// Always recycle and we will reset the view when it is bound
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public int getItemCount() {
|
|
return mApps.getAdapterItems().size();
|
|
}
|
|
|
|
@Override
|
|
public int getItemViewType(int position) {
|
|
AdapterItem item = mApps.getAdapterItems().get(position);
|
|
return item.viewType;
|
|
}
|
|
|
|
@NonNull
|
|
@Override
|
|
public Lifecycle getLifecycle() {
|
|
return mLifecycleRegistry;
|
|
}
|
|
}
|