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 android.content.Context;
20 import android.graphics.Canvas;
21 import android.graphics.Color;
22 import android.graphics.Paint;
23 import android.graphics.Point;
24 import android.graphics.Rect;
25 import android.graphics.drawable.Drawable;
26 import android.view.LayoutInflater;
27 import android.view.View;
28 import android.widget.TextView;
29 
30 import com.android.documentsui.base.DocumentInfo;
31 import com.android.documentsui.base.Shared;
32 import com.android.documentsui.dirlist.IconHelper;
33 import com.android.documentsui.selection.Selection;
34 
35 import java.util.List;
36 import java.util.function.Function;
37 
38 public final 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 int mPadding;
47     private Paint paint;
48 
DragShadowBuilder(Context context)49     public DragShadowBuilder(Context context) {
50         mWidth = context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_width);
51         mHeight = context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_height);
52         mShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_radius);
53         mPadding = context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_padding);
54 
55         mShadowView = LayoutInflater.from(context).inflate(R.layout.drag_shadow_layout, null);
56         mTitle = (TextView) mShadowView.findViewById(android.R.id.title);
57         mIcon = (DropBadgeView) mShadowView.findViewById(android.R.id.icon);
58 
59         // Important for certain APIs
60         mShadowView.setLayerType(View.LAYER_TYPE_SOFTWARE, paint);
61         paint = new Paint(Paint.ANTI_ALIAS_FLAG);
62     }
63 
64     @Override
onProvideShadowMetrics( Point shadowSize, Point shadowTouchPoint)65     public void onProvideShadowMetrics(
66             Point shadowSize, Point shadowTouchPoint) {
67         shadowSize.set(mWidth, mHeight);
68         shadowTouchPoint.set(mWidth, mHeight);
69     }
70 
71     @Override
onDrawShadow(Canvas canvas)72     public void onDrawShadow(Canvas canvas) {
73         Rect r = canvas.getClipBounds();
74         // Calling measure is necessary in order for all child views to get correctly laid out.
75         mShadowView.measure(
76                 View.MeasureSpec.makeMeasureSpec(r.right- r.left, View.MeasureSpec.EXACTLY),
77                 View.MeasureSpec.makeMeasureSpec(r.bottom - r.top , View.MeasureSpec.EXACTLY));
78         mShadowView.layout(r.left, r.top, r.right, r.bottom);
79 
80         // Since DragShadow is not an actual view drawn in hardware-accelerated window,
81         // android:elevation does not work; we need to draw the shadow ourselves manually.
82         paint.setColor(Color.TRANSPARENT);
83         // Shadow 1
84         int opacity = (int) (255 * 0.1);
85         paint.setShadowLayer(mShadowRadius, 0, 0, Color.argb(opacity, 0, 0, 0));
86         canvas.drawRect(r.left + mPadding, r.top + mPadding, r.right - mPadding,
87                 r.bottom - mPadding, paint);
88         // Shadow 2
89         opacity = (int) (255 * 0.24);
90         paint.setShadowLayer(mShadowRadius, 0, mShadowRadius, Color.argb(opacity, 0, 0, 0));
91         canvas.drawRect(r.left + mPadding, r.top + mPadding, r.right - mPadding,
92                 r.bottom - mPadding, paint);
93         mShadowView.draw(canvas);
94     }
95 
updateTitle(String title)96     public void updateTitle(String title) {
97         mTitle.setText(title);
98     }
99 
updateIcon(Drawable icon)100     public void updateIcon(Drawable icon) {
101         mIcon.updateIcon(icon);
102     }
103 
resetBackground()104     public void resetBackground() {
105         mIcon.setDropHovered(false);
106         mIcon.setEnabled(false);
107     }
108 
setAppearDroppable(boolean droppable)109     public void setAppearDroppable(boolean droppable) {
110         mIcon.setDropHovered(true);
111         mIcon.setDroppable(droppable);
112     }
113 
114     /**
115      * Provides a means of fully isolating the mechanics of building drag shadows (and builders)
116      * in support of testing.
117      */
118     public static final class Updater implements Function<Selection, DragShadowBuilder> {
119 
120         private final Context mContext;
121         private final IconHelper mIconHelper;
122         private final Drawable mDefaultDragIcon;
123         private final Model mModel;
124         private final DragShadowBuilder mShadowBuilder;
125 
Updater( Context context, DragShadowBuilder shadowBuilder, Model model, IconHelper iconHelper, Drawable defaultDragIcon)126         public Updater(
127                 Context context, DragShadowBuilder shadowBuilder, Model model,
128                 IconHelper iconHelper, Drawable defaultDragIcon) {
129             mContext = context;
130             mShadowBuilder = shadowBuilder;
131             mModel = model;
132             mIconHelper = iconHelper;
133             mDefaultDragIcon = defaultDragIcon;
134         }
135 
136         @Override
apply(Selection selection)137         public DragShadowBuilder apply(Selection selection) {
138             mShadowBuilder.updateTitle(getDragTitle(selection));
139             mShadowBuilder.updateIcon(getDragIcon(selection));
140 
141             return mShadowBuilder;
142         }
143 
getDragIcon(Selection selection)144         private Drawable getDragIcon(Selection selection) {
145             if (selection.size() == 1) {
146                 DocumentInfo doc = getSingleSelectedDocument(selection);
147                 return mIconHelper.getDocumentIcon(mContext, doc);
148             }
149             return mDefaultDragIcon;
150         }
151 
getDragTitle(Selection selection)152         private String getDragTitle(Selection selection) {
153             assert (!selection.isEmpty());
154             if (selection.size() == 1) {
155                 DocumentInfo doc = getSingleSelectedDocument(selection);
156                 return doc.displayName;
157             }
158             return Shared.getQuantityString(mContext, R.plurals.elements_dragged, selection.size());
159         }
160 
getSingleSelectedDocument(Selection selection)161         private DocumentInfo getSingleSelectedDocument(Selection selection) {
162             assert (selection.size() == 1);
163             final List<DocumentInfo> docs = mModel.getDocuments(selection);
164             assert (docs.size() == 1);
165             return docs.get(0);
166         }
167     }
168 }
169