• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.graphics.Canvas;
20 import android.graphics.Paint;
21 import android.graphics.Path;
22 import android.graphics.RectF;
23 import android.graphics.drawable.GradientDrawable;
24 import android.graphics.drawable.InsetDrawable;
25 import android.view.View;
26 
27 import androidx.annotation.Nullable;
28 
29 import com.android.launcher3.R;
30 
31 public class SectionDecorationHandler {
32 
33     protected final Path mTmpPath = new Path();
34     protected final RectF mTmpRect = new RectF();
35 
36     protected final int mCornerGroupRadius;
37     protected final int mCornerResultRadius;
38     protected final RectF mBounds = new RectF();
39     protected final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
40 
41     protected final int mFocusAlpha = 255; // main focused item alpha
42     protected int mFillColor; // grouping color
43     protected int mFocusColor; // main focused item color
44     protected float mFillSpacing;
45     protected int mInlineRadius;
46     protected Context mContext;
47     protected float[] mCorners;
48     protected int mFillAlpha;
49     protected boolean mIsTopLeftRound;
50     protected boolean mIsTopRightRound;
51     protected boolean mIsBottomLeftRound;
52     protected boolean mIsBottomRightRound;
53     protected boolean mIsBottomRound;
54     protected boolean mIsTopRound;
55 
SectionDecorationHandler(Context context, int fillAlpha, boolean isTopLeftRound, boolean isTopRightRound, boolean isBottomLeftRound, boolean isBottomRightRound)56     public SectionDecorationHandler(Context context, int fillAlpha, boolean isTopLeftRound,
57             boolean isTopRightRound, boolean isBottomLeftRound,
58             boolean isBottomRightRound) {
59 
60         mContext = context;
61         mFillAlpha = fillAlpha;
62         mFocusColor = context.getColor(R.color.materialColorSurfaceBright); // UX recommended
63         mFillColor = context.getColor(R.color.materialColorSurfaceContainerHigh); // UX recommended
64 
65         mIsTopLeftRound = isTopLeftRound;
66         mIsTopRightRound = isTopRightRound;
67         mIsBottomLeftRound = isBottomLeftRound;
68         mIsBottomRightRound = isBottomRightRound;
69         mIsBottomRound = mIsBottomLeftRound && mIsBottomRightRound;
70         mIsTopRound = mIsTopLeftRound && mIsTopRightRound;
71 
72         mCornerGroupRadius = context.getResources().getDimensionPixelSize(
73                 R.dimen.all_apps_recycler_view_decorator_group_radius);
74         mCornerResultRadius = context.getResources().getDimensionPixelSize(
75                 R.dimen.all_apps_recycler_view_decorator_result_radius);
76 
77         mInlineRadius = 0;
78         mFillSpacing = 0;
79         initCorners();
80     }
81 
initCorners()82     protected void initCorners() {
83         mCorners = new float[]{
84                 mIsTopLeftRound ? mCornerGroupRadius : 0,
85                 mIsTopLeftRound ? mCornerGroupRadius : 0, // Top left radius in px
86                 mIsTopRightRound ? mCornerGroupRadius : 0,
87                 mIsTopRightRound ? mCornerGroupRadius : 0, // Top right radius in px
88                 mIsBottomRightRound ? mCornerGroupRadius : 0,
89                 mIsBottomRightRound ? mCornerGroupRadius : 0, // Bottom right
90                 mIsBottomLeftRound ? mCornerGroupRadius : 0,
91                 mIsBottomLeftRound ? mCornerGroupRadius : 0 // Bottom left
92         };
93     }
94 
setFillAlpha(int fillAlpha)95     protected void setFillAlpha(int fillAlpha) {
96         mFillAlpha = fillAlpha;
97         mPaint.setAlpha(mFillAlpha);
98     }
99 
onFocusDraw(Canvas canvas, @Nullable View view)100     protected void onFocusDraw(Canvas canvas, @Nullable View view) {
101         if (view == null) {
102             return;
103         }
104         mPaint.setColor(mFillColor);
105         mPaint.setAlpha(mFillAlpha);
106         int scaledHeight = (int) (view.getHeight() * view.getScaleY());
107         mBounds.set(view.getLeft(), view.getY(), view.getRight(), view.getY() + scaledHeight);
108         onDraw(canvas);
109     }
110 
onDraw(Canvas canvas)111     protected void onDraw(Canvas canvas) {
112         mTmpPath.reset();
113         mTmpRect.set(mBounds.left + mFillSpacing,
114                 mBounds.top + mFillSpacing,
115                 mBounds.right - mFillSpacing,
116                 mBounds.bottom - mFillSpacing);
117         mTmpPath.addRoundRect(mTmpRect, mCorners, Path.Direction.CW);
118         canvas.drawPath(mTmpPath, mPaint);
119     }
120 
121     /** Sets the right background drawable to the view based on the give decoration info. */
applyBackground(View view, Context context, @Nullable SectionDecorationInfo decorationInfo, boolean isHighlighted)122     public void applyBackground(View view, Context context,
123            @Nullable SectionDecorationInfo decorationInfo, boolean isHighlighted) {
124         int inset = context.getResources().getDimensionPixelSize(
125                 R.dimen.all_apps_recycler_view_decorator_padding);
126         float radiusBottom = (decorationInfo == null || decorationInfo.isBottomRound()) ?
127                 mCornerGroupRadius : mCornerResultRadius;
128         float radiusTop =
129                 (decorationInfo == null || decorationInfo.isTopRound()) ?
130                         mCornerGroupRadius : mCornerResultRadius;
131         int color = isHighlighted ? mFocusColor : mFillColor;
132 
133         GradientDrawable shape = new GradientDrawable();
134         shape.setShape(GradientDrawable.RECTANGLE);
135         shape.setCornerRadii(new float[] {
136                 radiusTop, radiusTop, // top-left
137                 radiusTop, radiusTop, // top-right
138                 radiusBottom, radiusBottom, // bottom-right
139                 radiusBottom, radiusBottom // bottom-left
140         });
141         shape.setColor(color);
142 
143         // Setting the background resets the padding, so we cache it and reset it afterwards.
144         int paddingLeft = view.getPaddingLeft();
145         int paddingTop = view.getPaddingTop();
146         int paddingRight = view.getPaddingRight();
147         int paddingBottom = view.getPaddingBottom();
148 
149         view.setBackground(new InsetDrawable(shape, inset));
150 
151         view.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
152     }
153 
154     /**
155      * Section decorator that combines views and draws a single block decoration
156      */
157     public static class UnionDecorationHandler extends SectionDecorationHandler {
158 
159         private final int mPaddingLeft;
160         private final int mPaddingRight;
161 
UnionDecorationHandler( SectionDecorationHandler decorationHandler, int paddingLeft, int paddingRight)162         public UnionDecorationHandler(
163                 SectionDecorationHandler decorationHandler,
164                 int paddingLeft, int paddingRight) {
165             super(decorationHandler.mContext, decorationHandler.mFillAlpha,
166                     decorationHandler.mIsTopLeftRound, decorationHandler.mIsTopRightRound,
167                     decorationHandler.mIsBottomLeftRound, decorationHandler.mIsBottomRightRound);
168             mPaddingLeft = paddingLeft;
169             mPaddingRight = paddingRight;
170         }
171 
172         /**
173          * Expands decoration bounds to include child {@link PrivateAppsSectionDecorator}
174          */
addChild(SectionDecorationHandler child, View view)175         public void addChild(SectionDecorationHandler child, View view) {
176             int scaledHeight = (int) (view.getHeight() * view.getScaleY());
177             mBounds.union(view.getLeft(), view.getY(),
178                     view.getRight(), view.getY() + scaledHeight);
179             mIsBottomRound |= child.mIsBottomRound;
180             mIsBottomLeftRound |= child.mIsBottomLeftRound;
181             mIsBottomRightRound |= child.mIsBottomRightRound;
182             mIsTopRound |= child.mIsTopRound;
183             mIsTopLeftRound |= child.mIsTopLeftRound;
184             mIsTopRightRound |= child.mIsTopRightRound;
185         }
186 
187         /**
188          * Draws group decoration to canvas
189          */
onGroupDecorate(Canvas canvas)190         public void onGroupDecorate(Canvas canvas) {
191             initCorners();
192             mBounds.left = mPaddingLeft;
193             mBounds.right = canvas.getWidth() - mPaddingRight;
194             mPaint.setColor(mFillColor);
195             mPaint.setAlpha(mFillAlpha);
196             onDraw(canvas);
197         }
198     }
199 }
200