• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.documentsui;
18 
19 import static com.android.documentsui.util.FlagUtils.isUseMaterial3FlagEnabled;
20 
21 import android.content.Context;
22 import android.graphics.Canvas;
23 import android.graphics.Color;
24 import android.graphics.Paint;
25 import android.graphics.Point;
26 import android.graphics.Rect;
27 import android.graphics.drawable.Drawable;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.widget.TextView;
31 
32 import androidx.annotation.Nullable;
33 
34 import com.android.documentsui.DragAndDropManager.State;
35 
36 import java.text.NumberFormat;
37 
38 class DragShadowBuilder extends View.DragShadowBuilder {
39 
40     private final View mShadowView;
41     private final TextView mTitle;
42     private final DropBadgeView mIcon;
43     private final int mWidth;
44     private final int mHeight;
45     private final int mShadowRadius;
46     private final int mPadding;
47     private Paint paint;
48     // This will be null if use_material3 flag is OFF.
49     private final @Nullable View mAdditionalShadowView;
50     // This will always be 0 if the use_material3 flag is OFF.
51     private int mDragFileCount = 0;
52     // The following 5 dimensions will be 0 if the use_material3 flag is OFF.
53     private final int mDragContentRadius;
54     private final int mAdditionalLayerOffset;
55     private final int mDragFileCounterOffset;
56     private final int mShadow2Radius;
57     private final int mShadowYOffset;
58 
DragShadowBuilder(Context context)59     DragShadowBuilder(Context context) {
60         mWidth = context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_width);
61         mHeight = context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_height);
62         mShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_radius);
63         mPadding = context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_padding);
64 
65         mShadowView = LayoutInflater.from(context).inflate(R.layout.drag_shadow_layout, null);
66         mTitle = (TextView) mShadowView.findViewById(android.R.id.title);
67         mIcon = (DropBadgeView) mShadowView.findViewById(android.R.id.icon);
68         if (isUseMaterial3FlagEnabled()) {
69             mAdditionalShadowView =
70                     LayoutInflater.from(context).inflate(R.layout.additional_drag_shadow, null);
71             mDragContentRadius =
72                     context.getResources().getDimensionPixelSize(R.dimen.drag_content_radius);
73             mAdditionalLayerOffset =
74                     context.getResources()
75                             .getDimensionPixelSize(R.dimen.drag_additional_layer_offset);
76             mDragFileCounterOffset =
77                     context.getResources().getDimensionPixelSize(R.dimen.drag_file_counter_offset);
78             mShadow2Radius =
79                     context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_2_radius);
80             mShadowYOffset =
81                     context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_y_offset);
82         } else {
83             mAdditionalShadowView = null;
84             mDragContentRadius = 0;
85             mAdditionalLayerOffset = 0;
86             mDragFileCounterOffset = 0;
87             mShadow2Radius = 0;
88             mShadowYOffset = 0;
89         }
90 
91         // Important for certain APIs
92         mShadowView.setLayerType(View.LAYER_TYPE_SOFTWARE, paint);
93         paint = new Paint(Paint.ANTI_ALIAS_FLAG);
94     }
95 
96     @Override
onProvideShadowMetrics( Point shadowSize, Point shadowTouchPoint)97     public void onProvideShadowMetrics(
98             Point shadowSize, Point shadowTouchPoint) {
99         shadowSize.set(mWidth, mHeight);
100         shadowTouchPoint.set(mWidth, mHeight);
101     }
102 
103     @Override
onDrawShadow(Canvas canvas)104     public void onDrawShadow(Canvas canvas) {
105         Rect r = canvas.getClipBounds();
106         // Calling measure is necessary in order for all child views to get correctly laid out.
107         mShadowView.measure(
108                 View.MeasureSpec.makeMeasureSpec(r.right - r.left, View.MeasureSpec.EXACTLY),
109                 View.MeasureSpec.makeMeasureSpec(r.bottom - r.top , View.MeasureSpec.EXACTLY));
110         mShadowView.layout(r.left, r.top, r.right, r.bottom);
111 
112         // Since DragShadow is not an actual view drawn in hardware-accelerated window,
113         // android:elevation does not work; we need to draw the shadow ourselves manually.
114         paint.setColor(Color.TRANSPARENT);
115 
116         // Layers on the canvas (from bottom to top):
117         // 1. Two shadows for the additional drag layer (if drag file count > 1)
118         // 2. The additional layer view itself (if drag file count > 1)
119         // 3. Two shadows for the drag content layer (icon, title)
120         // 4. The drag content layer itself
121         final int shadowOneOpacity = (int) (255 * 0.15);
122         final int shadowTwoOpacity = (int) (255 * 0.30);
123         if (mAdditionalShadowView != null && mDragFileCount > 1) {
124             mAdditionalShadowView.measure(
125                     View.MeasureSpec.makeMeasureSpec(r.right - r.left, View.MeasureSpec.EXACTLY),
126                     View.MeasureSpec.makeMeasureSpec(r.bottom - r.top , View.MeasureSpec.EXACTLY));
127             mAdditionalShadowView.layout(r.left, r.top, r.right, r.bottom);
128             // Shadow 1
129             paint.setShadowLayer(
130                     mShadowRadius, 0, mShadowYOffset, Color.argb(shadowOneOpacity, 0, 0, 0));
131             canvas.drawRoundRect(
132                     r.left + mShadowRadius,
133                     r.top + mDragFileCounterOffset + mAdditionalLayerOffset,
134                     r.right - mDragFileCounterOffset - mAdditionalLayerOffset,
135                     r.bottom - mShadowRadius,
136                     mDragContentRadius,
137                     mDragContentRadius,
138                     paint);
139             // Shadow 2
140             paint.setShadowLayer(
141                     mShadow2Radius, 0, mShadowYOffset, Color.argb(shadowTwoOpacity, 0, 0, 0));
142             canvas.drawRoundRect(
143                     r.left + mShadowRadius,
144                     r.top + mDragFileCounterOffset + mAdditionalLayerOffset,
145                     r.right - mDragFileCounterOffset - mAdditionalLayerOffset,
146                     r.bottom - mShadowRadius,
147                     mDragContentRadius,
148                     mDragContentRadius,
149                     paint);
150             mAdditionalShadowView.draw(canvas);
151         }
152 
153         if (isUseMaterial3FlagEnabled()) {
154             // Shadow 1
155             paint.setShadowLayer(
156                     mShadowRadius, 0, mShadowYOffset, Color.argb(shadowOneOpacity, 0, 0, 0));
157             canvas.drawRoundRect(
158                     r.left + mShadowRadius + mAdditionalLayerOffset,
159                     r.top + mDragFileCounterOffset,
160                     r.right - mDragFileCounterOffset,
161                     r.bottom - mShadowRadius - mAdditionalLayerOffset,
162                     mDragContentRadius,
163                     mDragContentRadius,
164                     paint);
165             // Shadow 2
166             paint.setShadowLayer(
167                     mShadow2Radius, 0, mShadowYOffset, Color.argb(shadowTwoOpacity, 0, 0, 0));
168             canvas.drawRoundRect(
169                     r.left + mShadowRadius + mAdditionalLayerOffset,
170                     r.top + mDragFileCounterOffset,
171                     r.right - mDragFileCounterOffset,
172                     r.bottom - mShadowRadius - mAdditionalLayerOffset,
173                     mDragContentRadius,
174                     mDragContentRadius,
175                     paint);
176         } else {
177             // Shadow 1
178             int opacity = (int) (255 * 0.1);
179             paint.setShadowLayer(mShadowRadius, 0, 0, Color.argb(opacity, 0, 0, 0));
180             canvas.drawRect(r.left + mPadding, r.top + mPadding, r.right - mPadding,
181                     r.bottom - mPadding, paint);
182             // Shadow 2
183             opacity = (int) (255 * 0.24);
184             paint.setShadowLayer(mShadowRadius, 0, mShadowRadius, Color.argb(opacity, 0, 0, 0));
185             canvas.drawRect(r.left + mPadding, r.top + mPadding, r.right - mPadding,
186                     r.bottom - mPadding, paint);
187         }
188         mShadowView.draw(canvas);
189     }
190 
updateTitle(String title)191     void updateTitle(String title) {
192         mTitle.setText(title);
193     }
194 
updateIcon(Drawable icon)195     void updateIcon(Drawable icon) {
196         mIcon.updateIcon(icon);
197     }
198 
onStateUpdated(@tate int state)199     void onStateUpdated(@State int state) {
200         mIcon.updateState(state);
201     }
202 
updateDragFileCount(int count)203     void updateDragFileCount(int count) {
204         if (!isUseMaterial3FlagEnabled()) {
205             return;
206         }
207         mDragFileCount = count;
208         TextView dragFileCountView = mShadowView.findViewById(R.id.drag_file_counter);
209         if (dragFileCountView != null) {
210             dragFileCountView.setVisibility(count > 1 ? View.VISIBLE : View.GONE);
211             if (count > 1) {
212                 NumberFormat numberFormat = NumberFormat.getInstance();
213                 dragFileCountView.setText(numberFormat.format(count));
214             }
215         }
216     }
217 }
218