• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
20 
21 import android.annotation.TargetApi;
22 import android.content.Context;
23 import android.graphics.Canvas;
24 import android.graphics.Typeface;
25 import android.os.Build;
26 import android.text.Layout;
27 import android.text.StaticLayout;
28 import android.text.TextPaint;
29 import android.util.AttributeSet;
30 import android.view.View;
31 
32 import androidx.annotation.ColorInt;
33 import androidx.core.content.ContextCompat;
34 
35 import com.android.launcher3.R;
36 import com.android.launcher3.allapps.FloatingHeaderRow;
37 import com.android.launcher3.allapps.FloatingHeaderView;
38 import com.android.launcher3.util.OnboardingPrefs;
39 import com.android.launcher3.util.Themes;
40 import com.android.launcher3.views.ActivityContext;
41 
42 /**
43  * A view which shows a horizontal divider
44  */
45 @TargetApi(Build.VERSION_CODES.O)
46 public class AppsDividerView extends View implements FloatingHeaderRow {
47 
48     public enum DividerType {
49         NONE,
50         LINE,
51         ALL_APPS_LABEL
52     }
53 
54     private final TextPaint mPaint = new TextPaint();
55     private DividerType mDividerType = DividerType.NONE;
56 
57     private final @ColorInt int mStrokeColor;
58     private final @ColorInt int mAllAppsLabelTextColor;
59 
60     private Layout mAllAppsLabelLayout;
61     private boolean mShowAllAppsLabel;
62 
63     private FloatingHeaderView mParent;
64     private boolean mTabsHidden;
65     private FloatingHeaderRow[] mRows = FloatingHeaderRow.NO_ROWS;
66 
67     private boolean mIsScrolledOut = false;
68 
69     private final int[] mDividerSize;
70 
AppsDividerView(Context context)71     public AppsDividerView(Context context) {
72         this(context, null);
73     }
74 
AppsDividerView(Context context, AttributeSet attrs)75     public AppsDividerView(Context context, AttributeSet attrs) {
76         this(context, attrs, 0);
77     }
78 
AppsDividerView(Context context, AttributeSet attrs, int defStyleAttr)79     public AppsDividerView(Context context, AttributeSet attrs, int defStyleAttr) {
80         super(context, attrs, defStyleAttr);
81 
82         boolean isMainColorDark = Themes.getAttrBoolean(context, R.attr.isMainColorDark);
83         mDividerSize = new int[]{
84                 getResources().getDimensionPixelSize(R.dimen.all_apps_divider_width),
85                 getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height)
86         };
87 
88         mStrokeColor = ContextCompat.getColor(context, isMainColorDark
89                 ? R.color.all_apps_prediction_row_separator_dark
90                 : R.color.all_apps_prediction_row_separator);
91 
92         mAllAppsLabelTextColor = ContextCompat.getColor(context, isMainColorDark
93                 ? R.color.all_apps_label_text_dark
94                 : R.color.all_apps_label_text);
95 
96         OnboardingPrefs<?> onboardingPrefs = ActivityContext.lookupContext(
97                 getContext()).getOnboardingPrefs();
98         mShowAllAppsLabel = onboardingPrefs == null || !onboardingPrefs.hasReachedMaxCount(
99                 ALL_APPS_VISITED_COUNT);
100     }
101 
setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden)102     public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
103         mParent = parent;
104         mTabsHidden = tabsHidden;
105         mRows = rows;
106         updateDividerType();
107     }
108 
109     /** {@code true} if all apps label should be shown in place of divider. */
setShowAllAppsLabel(boolean showAllAppsLabel)110     public void setShowAllAppsLabel(boolean showAllAppsLabel) {
111         if (showAllAppsLabel != mShowAllAppsLabel) {
112             mShowAllAppsLabel = showAllAppsLabel;
113             updateDividerType();
114         }
115     }
116 
117     @Override
getExpectedHeight()118     public int getExpectedHeight() {
119         return getPaddingTop() + getPaddingBottom();
120     }
121 
122     @Override
shouldDraw()123     public boolean shouldDraw() {
124         return mDividerType != DividerType.NONE;
125     }
126 
127     @Override
hasVisibleContent()128     public boolean hasVisibleContent() {
129         return false;
130     }
131 
updateDividerType()132     private void updateDividerType() {
133         final DividerType dividerType;
134         if (!mTabsHidden) {
135             dividerType = DividerType.NONE;
136         } else {
137             // Check how many sections above me.
138             int sectionCount = 0;
139             for (FloatingHeaderRow row : mRows) {
140                 if (row == this) {
141                     break;
142                 } else if (row.shouldDraw()) {
143                     sectionCount++;
144                 }
145             }
146 
147             if (mShowAllAppsLabel && sectionCount > 0) {
148                 dividerType = DividerType.ALL_APPS_LABEL;
149             } else if (sectionCount == 1) {
150                 dividerType = DividerType.LINE;
151             } else {
152                 dividerType = DividerType.NONE;
153             }
154         }
155 
156         if (mDividerType != dividerType) {
157             mDividerType = dividerType;
158             int topPadding;
159             int bottomPadding;
160             switch (dividerType) {
161                 case LINE:
162                     topPadding = 0;
163                     bottomPadding = getResources()
164                             .getDimensionPixelSize(R.dimen.all_apps_prediction_row_divider_height);
165                     mPaint.setColor(mStrokeColor);
166                     break;
167                 case ALL_APPS_LABEL:
168                     topPadding = getAllAppsLabelLayout().getHeight() + getResources()
169                             .getDimensionPixelSize(R.dimen.all_apps_label_top_padding);
170                     bottomPadding = getResources()
171                             .getDimensionPixelSize(R.dimen.all_apps_label_bottom_padding);
172                     mPaint.setColor(mAllAppsLabelTextColor);
173                     break;
174                 case NONE:
175                 default:
176                     topPadding = bottomPadding = 0;
177                     break;
178             }
179             setPadding(getPaddingLeft(), topPadding, getPaddingRight(), bottomPadding);
180             updateViewVisibility();
181             invalidate();
182             requestLayout();
183             if (mParent != null) {
184                 mParent.onHeightUpdated();
185             }
186         }
187     }
188 
updateViewVisibility()189     private void updateViewVisibility() {
190         setVisibility(mDividerType == DividerType.NONE
191                 ? GONE
192                 : (mIsScrolledOut ? INVISIBLE : VISIBLE));
193     }
194 
195     @Override
onDraw(Canvas canvas)196     protected void onDraw(Canvas canvas) {
197         if (mDividerType == DividerType.LINE) {
198             int l = (getWidth() - mDividerSize[0]) / 2;
199             int t = getHeight() - (getPaddingBottom() / 2);
200             int radius = mDividerSize[1];
201             canvas.drawRoundRect(l, t, l + mDividerSize[0], t + mDividerSize[1], radius, radius,
202                     mPaint);
203         } else if (mDividerType == DividerType.ALL_APPS_LABEL) {
204             Layout textLayout = getAllAppsLabelLayout();
205             int x = getWidth() / 2 - textLayout.getWidth() / 2;
206             int y = getHeight() - getPaddingBottom() - textLayout.getHeight();
207             canvas.translate(x, y);
208             textLayout.draw(canvas);
209             canvas.translate(-x, -y);
210         }
211     }
212 
getAllAppsLabelLayout()213     private Layout getAllAppsLabelLayout() {
214         if (mAllAppsLabelLayout == null) {
215             mPaint.setAntiAlias(true);
216             mPaint.setTypeface(Typeface.create("google-sans", Typeface.NORMAL));
217             mPaint.setTextSize(
218                     getResources().getDimensionPixelSize(R.dimen.all_apps_label_text_size));
219 
220             CharSequence allAppsLabelText = getResources().getText(R.string.all_apps_label);
221             mAllAppsLabelLayout = StaticLayout.Builder.obtain(
222                             allAppsLabelText, 0, allAppsLabelText.length(), mPaint,
223                             Math.round(mPaint.measureText(allAppsLabelText.toString())))
224                     .setAlignment(Layout.Alignment.ALIGN_CENTER)
225                     .setMaxLines(1)
226                     .setIncludePad(true)
227                     .build();
228         }
229         return mAllAppsLabelLayout;
230     }
231 
232     @Override
hasOverlappingRendering()233     public boolean hasOverlappingRendering() {
234         return false;
235     }
236 
onMeasure(int widthMeasureSpec, int heightMeasureSpec)237     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
238         setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
239                 getPaddingBottom() + getPaddingTop());
240     }
241 
242     @Override
setVerticalScroll(int scroll, boolean isScrolledOut)243     public void setVerticalScroll(int scroll, boolean isScrolledOut) {
244         setTranslationY(scroll);
245         mIsScrolledOut = isScrolledOut;
246         updateViewVisibility();
247     }
248 
249     @Override
getTypeClass()250     public Class<AppsDividerView> getTypeClass() {
251         return AppsDividerView.class;
252     }
253 
254     @Override
getFocusedChild()255     public View getFocusedChild() {
256         return null;
257     }
258 }
259