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