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