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.launcher3.shortcuts;
18 
19 import android.content.Context;
20 import android.content.pm.ShortcutInfo;
21 import android.content.res.ColorStateList;
22 import android.graphics.Color;
23 import android.graphics.Point;
24 import android.graphics.drawable.ColorDrawable;
25 import android.graphics.drawable.Drawable;
26 import android.graphics.drawable.GradientDrawable;
27 import android.graphics.drawable.RippleDrawable;
28 import android.text.TextUtils;
29 import android.util.AttributeSet;
30 import android.view.View;
31 import android.widget.FrameLayout;
32 
33 import com.android.launcher3.BubbleTextView;
34 import com.android.launcher3.Launcher;
35 import com.android.launcher3.R;
36 import com.android.launcher3.Utilities;
37 import com.android.launcher3.model.data.WorkspaceItemInfo;
38 import com.android.launcher3.popup.PopupContainerWithArrow;
39 import com.android.launcher3.util.Themes;
40 import com.android.launcher3.views.BubbleTextHolder;
41 
42 /**
43  * A {@link android.widget.FrameLayout} that contains an icon and a {@link BubbleTextView} for text.
44  * This lets us animate the child BubbleTextView's background (transparent ripple) separately from
45  * the {@link DeepShortcutView} background color.
46  */
47 public class DeepShortcutView extends FrameLayout implements BubbleTextHolder {
48 
49     private static final Point sTempPoint = new Point();
50 
51     private final Drawable mTransparentDrawable = new ColorDrawable(Color.TRANSPARENT);
52 
53     private BubbleTextView mBubbleText;
54     private View mIconView;
55 
56     private WorkspaceItemInfo mInfo;
57     private ShortcutInfo mDetail;
58 
DeepShortcutView(Context context)59     public DeepShortcutView(Context context) {
60         this(context, null, 0);
61     }
62 
DeepShortcutView(Context context, AttributeSet attrs)63     public DeepShortcutView(Context context, AttributeSet attrs) {
64         this(context, attrs, 0);
65     }
66 
DeepShortcutView(Context context, AttributeSet attrs, int defStyle)67     public DeepShortcutView(Context context, AttributeSet attrs, int defStyle) {
68         super(context, attrs, defStyle);
69     }
70 
71     @Override
onFinishInflate()72     protected void onFinishInflate() {
73         super.onFinishInflate();
74         mBubbleText = findViewById(R.id.bubble_text);
75         mBubbleText.setHideBadge(true);
76         mIconView = findViewById(R.id.icon);
77         tryUpdateTextBackground();
78     }
79 
80     @Override
setBackground(Drawable background)81     public void setBackground(Drawable background) {
82         super.setBackground(background);
83         tryUpdateTextBackground();
84     }
85 
86     @Override
setBackgroundResource(int resid)87     public void setBackgroundResource(int resid) {
88         super.setBackgroundResource(resid);
89         tryUpdateTextBackground();
90     }
91 
92     /**
93      * Updates the text background to match the shape of this background (when applicable).
94      */
tryUpdateTextBackground()95     private void tryUpdateTextBackground() {
96         if (!(getBackground() instanceof GradientDrawable) || mBubbleText == null) {
97             return;
98         }
99         GradientDrawable background = (GradientDrawable) getBackground();
100 
101         int color = Themes.getAttrColor(getContext(), android.R.attr.colorControlHighlight);
102         GradientDrawable backgroundMask = new GradientDrawable();
103         backgroundMask.setColor(color);
104         backgroundMask.setShape(GradientDrawable.RECTANGLE);
105         if (background.getCornerRadii() != null) {
106             backgroundMask.setCornerRadii(background.getCornerRadii());
107         } else {
108             backgroundMask.setCornerRadius(background.getCornerRadius());
109         }
110 
111         RippleDrawable drawable = new RippleDrawable(ColorStateList.valueOf(color),
112                 mTransparentDrawable, backgroundMask);
113         mBubbleText.setBackground(drawable);
114     }
115 
116     @Override
getBubbleText()117     public BubbleTextView getBubbleText() {
118         return mBubbleText;
119     }
120 
setWillDrawIcon(boolean willDraw)121     public void setWillDrawIcon(boolean willDraw) {
122         mIconView.setVisibility(willDraw ? View.VISIBLE : View.INVISIBLE);
123     }
124 
willDrawIcon()125     public boolean willDrawIcon() {
126         return mIconView.getVisibility() == View.VISIBLE;
127     }
128 
129     /**
130      * Returns the position of the center of the icon relative to the container.
131      */
getIconCenter()132     public Point getIconCenter() {
133         sTempPoint.y = sTempPoint.x = getMeasuredHeight() / 2;
134         if (Utilities.isRtl(getResources())) {
135             sTempPoint.x = getMeasuredWidth() - sTempPoint.x;
136         }
137         return sTempPoint;
138     }
139 
140     /** package private **/
applyShortcutInfo(WorkspaceItemInfo info, ShortcutInfo detail, PopupContainerWithArrow container)141     public void applyShortcutInfo(WorkspaceItemInfo info, ShortcutInfo detail,
142             PopupContainerWithArrow container) {
143         mInfo = info;
144         mDetail = detail;
145         mBubbleText.applyFromWorkspaceItem(info);
146         mIconView.setBackground(mBubbleText.getIcon());
147 
148         // Use the long label as long as it exists and fits.
149         CharSequence longLabel = mDetail.getLongLabel();
150         int availableWidth = mBubbleText.getWidth() - mBubbleText.getTotalPaddingLeft()
151                 - mBubbleText.getTotalPaddingRight();
152         boolean usingLongLabel = !TextUtils.isEmpty(longLabel)
153                 && mBubbleText.getPaint().measureText(longLabel.toString()) <= availableWidth;
154         mBubbleText.setText(usingLongLabel ? longLabel : mDetail.getShortLabel());
155 
156         // TODO: Add the click handler to this view directly and not the child view.
157         mBubbleText.setOnClickListener(container.getItemClickListener());
158         mBubbleText.setOnLongClickListener(container.getItemDragHandler());
159         mBubbleText.setOnTouchListener(container.getItemDragHandler());
160     }
161 
162     /**
163      * Returns the shortcut info that is suitable to be added on the homescreen
164      */
getFinalInfo()165     public WorkspaceItemInfo getFinalInfo() {
166         final WorkspaceItemInfo badged = new WorkspaceItemInfo(mInfo);
167         // Queue an update task on the worker thread. This ensures that the badged
168         // shortcut eventually gets its icon updated.
169         Launcher.getLauncher(getContext()).getModel()
170                 .updateAndBindWorkspaceItem(badged, mDetail);
171         return badged;
172     }
173 
getIconView()174     public View getIconView() {
175         return mIconView;
176     }
177 
getDetail()178     public ShortcutInfo getDetail() {
179         return mDetail;
180     }
181 }
182