• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 
17 package com.android.launcher3.appprediction;
18 
19 import android.annotation.TargetApi;
20 import android.content.Context;
21 import android.graphics.Canvas;
22 import android.os.Build;
23 import android.util.AttributeSet;
24 import android.view.LayoutInflater;
25 import android.view.View;
26 import android.widget.LinearLayout;
27 
28 import androidx.annotation.NonNull;
29 import androidx.annotation.Nullable;
30 
31 import com.android.launcher3.BubbleTextView;
32 import com.android.launcher3.DeviceProfile;
33 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
34 import com.android.launcher3.R;
35 import com.android.launcher3.Utilities;
36 import com.android.launcher3.allapps.FloatingHeaderRow;
37 import com.android.launcher3.allapps.FloatingHeaderView;
38 import com.android.launcher3.anim.AlphaUpdateListener;
39 import com.android.launcher3.keyboard.FocusIndicatorHelper;
40 import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
41 import com.android.launcher3.model.data.ItemInfo;
42 import com.android.launcher3.model.data.ItemInfoWithIcon;
43 import com.android.launcher3.model.data.WorkspaceItemInfo;
44 import com.android.launcher3.touch.ItemLongClickListener;
45 import com.android.launcher3.views.ActivityContext;
46 
47 import java.util.ArrayList;
48 import java.util.List;
49 import java.util.stream.Collectors;
50 
51 @TargetApi(Build.VERSION_CODES.P)
52 public class PredictionRowView<T extends Context & ActivityContext>
53         extends LinearLayout implements OnDeviceProfileChangeListener, FloatingHeaderRow {
54 
55     private final T mActivityContext;
56     private int mNumPredictedAppsPerRow;
57 
58     // Helper to drawing the focus indicator.
59     private final FocusIndicatorHelper mFocusHelper;
60 
61     // The set of predicted apps resolved from the component names and the current set of apps
62     private final List<WorkspaceItemInfo> mPredictedApps = new ArrayList<>();
63 
64     private FloatingHeaderView mParent;
65 
66     private boolean mPredictionsEnabled = false;
67     private OnLongClickListener mOnIconLongClickListener = ItemLongClickListener.INSTANCE_ALL_APPS;
68 
PredictionRowView(@onNull Context context)69     public PredictionRowView(@NonNull Context context) {
70         this(context, null);
71     }
72 
PredictionRowView(@onNull Context context, @Nullable AttributeSet attrs)73     public PredictionRowView(@NonNull Context context, @Nullable AttributeSet attrs) {
74         super(context, attrs);
75         setOrientation(LinearLayout.HORIZONTAL);
76 
77         mFocusHelper = new SimpleFocusIndicatorHelper(this);
78         mActivityContext = ActivityContext.lookupContext(context);
79         mNumPredictedAppsPerRow = mActivityContext.getDeviceProfile().numShownAllAppsColumns;
80         updateVisibility();
81     }
82 
83     @Override
onAttachedToWindow()84     protected void onAttachedToWindow() {
85         super.onAttachedToWindow();
86         mActivityContext.addOnDeviceProfileChangeListener(this);
87     }
88 
89     @Override
onDetachedFromWindow()90     protected void onDetachedFromWindow() {
91         super.onDetachedFromWindow();
92         mActivityContext.removeOnDeviceProfileChangeListener(this);
93     }
94 
setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden)95     public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
96         mParent = parent;
97     }
98 
updateVisibility()99     private void updateVisibility() {
100         setVisibility(mPredictionsEnabled ? VISIBLE : GONE);
101         if (mActivityContext.getAppsView() != null) {
102             if (mPredictionsEnabled) {
103                 mActivityContext.getAppsView().getAppsStore().registerIconContainer(this);
104             } else {
105                 mActivityContext.getAppsView().getAppsStore().unregisterIconContainer(this);
106             }
107         }
108     }
109 
110     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)111     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
112         super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(getExpectedHeight(),
113                 MeasureSpec.EXACTLY));
114     }
115 
116     @Override
dispatchDraw(Canvas canvas)117     protected void dispatchDraw(Canvas canvas) {
118         mFocusHelper.draw(canvas);
119         super.dispatchDraw(canvas);
120     }
121 
122     @Override
getExpectedHeight()123     public int getExpectedHeight() {
124         DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
125         int iconHeight = deviceProfile.allAppsIconSizePx;
126         int iconPadding = deviceProfile.allAppsIconDrawablePaddingPx;
127         int textHeight = Utilities.calculateTextHeight(deviceProfile.allAppsIconTextSizePx);
128         int verticalPadding = getResources().getDimensionPixelSize(
129                 R.dimen.all_apps_predicted_icon_vertical_padding);
130         int totalHeight = iconHeight + iconPadding + textHeight + verticalPadding * 2;
131         return getVisibility() == GONE ? 0 : totalHeight + getPaddingTop() + getPaddingBottom();
132     }
133 
134     @Override
shouldDraw()135     public boolean shouldDraw() {
136         return getVisibility() != GONE;
137     }
138 
139     @Override
hasVisibleContent()140     public boolean hasVisibleContent() {
141         return mPredictionsEnabled;
142     }
143 
144     @Override
isVisible()145     public boolean isVisible() {
146         return getVisibility() == VISIBLE;
147     }
148 
149     /**
150      * Returns the predicted apps.
151      */
getPredictedApps()152     public List<ItemInfoWithIcon> getPredictedApps() {
153         return new ArrayList<>(mPredictedApps);
154     }
155 
156     /**
157      * Sets the current set of predicted apps.
158      *
159      * This can be called before we get the full set of applications, we should merge the results
160      * only in onPredictionsUpdated() which is idempotent.
161      *
162      * If the number of predicted apps is the same as the previous list of predicted apps,
163      * we can optimize by swapping them in place.
164      */
setPredictedApps(List<ItemInfo> items)165     public void setPredictedApps(List<ItemInfo> items) {
166         applyPredictedApps(items);
167     }
168 
applyPredictedApps(List<ItemInfo> items)169     private void applyPredictedApps(List<ItemInfo> items) {
170         mPredictedApps.clear();
171         mPredictedApps.addAll(items.stream()
172                 .filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo)
173                 .map(itemInfo -> (WorkspaceItemInfo) itemInfo).collect(Collectors.toList()));
174         applyPredictionApps();
175     }
176 
177     /**
178      * Sets the long click listener for predictions for any future predictions.
179      *
180      * Existing predictions in the container are not updated with this new callback.
181      */
setOnIconLongClickListener(OnLongClickListener onIconLongClickListener)182     public void setOnIconLongClickListener(OnLongClickListener onIconLongClickListener) {
183         mOnIconLongClickListener = onIconLongClickListener;
184     }
185 
186     @Override
onDeviceProfileChanged(DeviceProfile dp)187     public void onDeviceProfileChanged(DeviceProfile dp) {
188         mNumPredictedAppsPerRow = dp.numShownAllAppsColumns;
189         removeAllViews();
190         applyPredictionApps();
191     }
192 
applyPredictionApps()193     private void applyPredictionApps() {
194         if (getChildCount() != mNumPredictedAppsPerRow) {
195             while (getChildCount() > mNumPredictedAppsPerRow) {
196                 removeViewAt(0);
197             }
198             LayoutInflater inflater = mActivityContext.getAppsView().getLayoutInflater();
199             while (getChildCount() < mNumPredictedAppsPerRow) {
200                 BubbleTextView icon = (BubbleTextView) inflater.inflate(
201                         R.layout.all_apps_icon, this, false);
202                 icon.setOnClickListener(mActivityContext.getItemOnClickListener());
203                 icon.setOnLongClickListener(mOnIconLongClickListener);
204                 icon.setLongPressTimeoutFactor(1f);
205                 icon.setOnFocusChangeListener(mFocusHelper);
206 
207                 LayoutParams lp = (LayoutParams) icon.getLayoutParams();
208                 // Ensure the all apps icon height matches the workspace icons in portrait mode.
209                 lp.height = mActivityContext.getDeviceProfile().allAppsCellHeightPx;
210                 lp.width = 0;
211                 lp.weight = 1;
212                 addView(icon);
213             }
214         }
215 
216         int predictionCount = mPredictedApps.size();
217 
218         for (int i = 0; i < getChildCount(); i++) {
219             BubbleTextView icon = (BubbleTextView) getChildAt(i);
220             icon.reset();
221             if (predictionCount > i) {
222                 icon.setVisibility(View.VISIBLE);
223                 icon.applyFromWorkspaceItem(mPredictedApps.get(i));
224             } else {
225                 icon.setVisibility(predictionCount == 0 ? GONE : INVISIBLE);
226             }
227         }
228 
229         boolean predictionsEnabled = predictionCount > 0;
230         if (predictionsEnabled != mPredictionsEnabled) {
231             mPredictionsEnabled = predictionsEnabled;
232             updateVisibility();
233         }
234         mParent.onHeightUpdated();
235     }
236 
237     @Override
hasOverlappingRendering()238     public boolean hasOverlappingRendering() {
239         return false;
240     }
241 
242 
243     @Override
setVerticalScroll(int scroll, boolean isScrolledOut)244     public void setVerticalScroll(int scroll, boolean isScrolledOut) {
245         if (!isScrolledOut) {
246             setTranslationY(scroll);
247         }
248         setAlpha(isScrolledOut ? 0 : 1);
249         if (getVisibility() != GONE) {
250             AlphaUpdateListener.updateVisibility(this);
251         }
252     }
253 
254     @Override
getTypeClass()255     public Class<PredictionRowView> getTypeClass() {
256         return PredictionRowView.class;
257     }
258 
259     @Override
getFocusedChild()260     public View getFocusedChild() {
261         return getChildAt(0);
262     }
263 
264 }
265