• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.launcher3.allapps;
17 
18 import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APPS;
19 
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.view.LayoutInflater;
23 import android.view.View;
24 import android.view.View.OnClickListener;
25 import android.view.View.OnFocusChangeListener;
26 import android.view.View.OnLongClickListener;
27 import android.view.ViewGroup;
28 import android.widget.TextView;
29 
30 import androidx.annotation.Nullable;
31 import androidx.recyclerview.widget.RecyclerView;
32 
33 import com.android.launcher3.BubbleTextView;
34 import com.android.launcher3.R;
35 import com.android.launcher3.allapps.search.SearchAdapterProvider;
36 import com.android.launcher3.Utilities;
37 import com.android.launcher3.config.FeatureFlags;
38 import com.android.launcher3.model.data.AppInfo;
39 import com.android.launcher3.views.ActivityContext;
40 
41 /**
42  * Adapter for all the apps.
43  *
44  * @param <T> Type of context inflating all apps.
45  */
46 public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> extends
47         RecyclerView.Adapter<BaseAllAppsAdapter.ViewHolder> {
48 
49     public static final String TAG = "BaseAllAppsAdapter";
50 
51     // A normal icon
52     public static final int VIEW_TYPE_ICON = 1 << 1;
53     // The message shown when there are no filtered results
54     public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 2;
55     // A divider that separates the apps list and the search market button
56     public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 3;
57 
58     public static final int VIEW_TYPE_WORK_EDU_CARD = 1 << 4;
59     public static final int VIEW_TYPE_WORK_DISABLED_CARD = 1 << 5;
60 
61     public static final int NEXT_ID = 6;
62 
63     // Common view type masks
64     public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
65     public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
66 
67     protected final SearchAdapterProvider<?> mAdapterProvider;
68 
69     /**
70      * ViewHolder for each icon.
71      */
72     public static class ViewHolder extends RecyclerView.ViewHolder {
73 
ViewHolder(View v)74         public ViewHolder(View v) {
75             super(v);
76         }
77     }
78 
79     /** Sets the number of apps to be displayed in one row of the all apps screen. */
setAppsPerRow(int appsPerRow)80     public abstract void setAppsPerRow(int appsPerRow);
81 
82     /**
83      * Info about a particular adapter item (can be either section or app)
84      */
85     public static class AdapterItem {
86         /** Common properties */
87         // The type of this item
88         public final int viewType;
89 
90         // The row that this item shows up on
91         public int rowIndex;
92         // The index of this app in the row
93         public int rowAppIndex;
94         // The associated ItemInfoWithIcon for the item
95         public AppInfo itemInfo = null;
96 
AdapterItem(int viewType)97         public AdapterItem(int viewType) {
98             this.viewType = viewType;
99         }
100 
101         /**
102          * Factory method for AppIcon AdapterItem
103          */
asApp(AppInfo appInfo)104         public static AdapterItem asApp(AppInfo appInfo) {
105             AdapterItem item = new AdapterItem(VIEW_TYPE_ICON);
106             item.itemInfo = appInfo;
107             return item;
108         }
109 
isCountedForAccessibility()110         protected boolean isCountedForAccessibility() {
111             return viewType == VIEW_TYPE_ICON;
112         }
113 
114         /**
115          * Returns true if the items represent the same object
116          */
isSameAs(AdapterItem other)117         public boolean isSameAs(AdapterItem other) {
118             return (other.viewType == viewType) && (other.getClass() == getClass());
119         }
120 
121         /**
122          * This is called only if {@link #isSameAs} returns true to check if the contents are same
123          * as well. Returning true will prevent redrawing of thee item.
124          */
isContentSame(AdapterItem other)125         public boolean isContentSame(AdapterItem other) {
126             return itemInfo == null && other.itemInfo == null;
127         }
128 
129         /** Sets the alpha of the decorator for this item. Returns true if successful. */
setDecorationFillAlpha(int alpha)130         public boolean setDecorationFillAlpha(int alpha) {
131             return false;
132         }
133     }
134 
135     protected final T mActivityContext;
136     protected final AlphabeticalAppsList<T> mApps;
137     // The text to show when there are no search results and no market search handler.
138     protected int mAppsPerRow;
139 
140     protected final LayoutInflater mLayoutInflater;
141     protected final OnClickListener mOnIconClickListener;
142     protected OnLongClickListener mOnIconLongClickListener = INSTANCE_ALL_APPS;
143     protected OnFocusChangeListener mIconFocusListener;
144     private final int mExtraTextHeight;
145 
BaseAllAppsAdapter(T activityContext, LayoutInflater inflater, AlphabeticalAppsList<T> apps, SearchAdapterProvider<?> adapterProvider)146     public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater,
147             AlphabeticalAppsList<T> apps, SearchAdapterProvider<?> adapterProvider) {
148         Resources res = activityContext.getResources();
149         mActivityContext = activityContext;
150         mApps = apps;
151         mLayoutInflater = inflater;
152 
153         mOnIconClickListener = mActivityContext.getItemOnClickListener();
154 
155         mAdapterProvider = adapterProvider;
156         mExtraTextHeight = Utilities.calculateTextHeight(
157                 mActivityContext.getDeviceProfile().allAppsIconTextSizePx);
158     }
159 
160     /**
161      * Sets the long click listener for icons
162      */
setOnIconLongClickListener(@ullable OnLongClickListener listener)163     public void setOnIconLongClickListener(@Nullable OnLongClickListener listener) {
164         mOnIconLongClickListener = listener;
165     }
166 
167     /** Checks if the passed viewType represents all apps divider. */
isDividerViewType(int viewType)168     public static boolean isDividerViewType(int viewType) {
169         return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER);
170     }
171 
172     /** Checks if the passed viewType represents all apps icon. */
isIconViewType(int viewType)173     public static boolean isIconViewType(int viewType) {
174         return isViewType(viewType, VIEW_TYPE_MASK_ICON);
175     }
176 
setIconFocusListener(OnFocusChangeListener focusListener)177     public void setIconFocusListener(OnFocusChangeListener focusListener) {
178         mIconFocusListener = focusListener;
179     }
180 
181     /**
182      * Returns the layout manager.
183      */
getLayoutManager()184     public abstract RecyclerView.LayoutManager getLayoutManager();
185 
186     @Override
onCreateViewHolder(ViewGroup parent, int viewType)187     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
188         switch (viewType) {
189             case VIEW_TYPE_ICON:
190                 int layout = !FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get() ? R.layout.all_apps_icon
191                         : R.layout.all_apps_icon_twoline;
192                 BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
193                         layout, parent, false);
194                 icon.setLongPressTimeoutFactor(1f);
195                 icon.setOnFocusChangeListener(mIconFocusListener);
196                 icon.setOnClickListener(mOnIconClickListener);
197                 icon.setOnLongClickListener(mOnIconLongClickListener);
198                 // Ensure the all apps icon height matches the workspace icons in portrait mode.
199                 icon.getLayoutParams().height =
200                         mActivityContext.getDeviceProfile().allAppsCellHeightPx;
201                 if (FeatureFlags.ENABLE_TWOLINE_ALLAPPS.get()) {
202                     icon.getLayoutParams().height += mExtraTextHeight;
203                 }
204                 return new ViewHolder(icon);
205             case VIEW_TYPE_EMPTY_SEARCH:
206                 return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
207                         parent, false));
208             case VIEW_TYPE_ALL_APPS_DIVIDER:
209                 return new ViewHolder(mLayoutInflater.inflate(
210                         R.layout.all_apps_divider, parent, false));
211             case VIEW_TYPE_WORK_EDU_CARD:
212                 return new ViewHolder(mLayoutInflater.inflate(
213                         R.layout.work_apps_edu, parent, false));
214             case VIEW_TYPE_WORK_DISABLED_CARD:
215                 return new ViewHolder(mLayoutInflater.inflate(
216                         R.layout.work_apps_paused, parent, false));
217             default:
218                 if (mAdapterProvider.isViewSupported(viewType)) {
219                     return mAdapterProvider.onCreateViewHolder(mLayoutInflater, parent, viewType);
220                 }
221                 throw new RuntimeException("Unexpected view type" + viewType);
222         }
223     }
224 
225     @Override
onBindViewHolder(ViewHolder holder, int position)226     public void onBindViewHolder(ViewHolder holder, int position) {
227         switch (holder.getItemViewType()) {
228             case VIEW_TYPE_ICON: {
229                 AdapterItem adapterItem = mApps.getAdapterItems().get(position);
230                 BubbleTextView icon = (BubbleTextView) holder.itemView;
231                 icon.reset();
232                 icon.applyFromApplicationInfo(adapterItem.itemInfo);
233                 break;
234             }
235             case VIEW_TYPE_EMPTY_SEARCH: {
236                 AppInfo info = mApps.getAdapterItems().get(position).itemInfo;
237                 if (info != null) {
238                     ((TextView) holder.itemView).setText(mActivityContext.getString(
239                             R.string.all_apps_no_search_results, info.title));
240                 }
241                 break;
242             }
243             case VIEW_TYPE_ALL_APPS_DIVIDER:
244             case VIEW_TYPE_WORK_DISABLED_CARD:
245                 // nothing to do
246                 break;
247             case VIEW_TYPE_WORK_EDU_CARD:
248                 ((WorkEduCard) holder.itemView).setPosition(position);
249                 break;
250             default:
251                 if (mAdapterProvider.isViewSupported(holder.getItemViewType())) {
252                     mAdapterProvider.onBindView(holder, position);
253                 }
254         }
255     }
256 
257     @Override
onFailedToRecycleView(ViewHolder holder)258     public boolean onFailedToRecycleView(ViewHolder holder) {
259         // Always recycle and we will reset the view when it is bound
260         return true;
261     }
262 
263     @Override
getItemCount()264     public int getItemCount() {
265         return mApps.getAdapterItems().size();
266     }
267 
268     @Override
getItemViewType(int position)269     public int getItemViewType(int position) {
270         AdapterItem item = mApps.getAdapterItems().get(position);
271         return item.viewType;
272     }
273 
isViewType(int viewType, int viewTypeMask)274     protected static boolean isViewType(int viewType, int viewTypeMask) {
275         return (viewType & viewTypeMask) != 0;
276     }
277 
278 }
279