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.res.Resources;
21 import android.graphics.Canvas;
22 import android.graphics.Rect;
23 import android.graphics.drawable.Drawable;
24 import android.text.TextUtils;
25 import android.util.AttributeSet;
26 import android.widget.Toast;
27 
28 import com.android.launcher3.BubbleTextView;
29 import com.android.launcher3.R;
30 import com.android.launcher3.Utilities;
31 
32 /**
33  * A {@link BubbleTextView} that has the shortcut icon on the left and drag handle on the right.
34  */
35 public class DeepShortcutTextView extends BubbleTextView {
36     private final Rect mDragHandleBounds = new Rect();
37     private final int mDragHandleWidth;
38     private boolean mShowInstructionToast = false;
39 
40     private Toast mInstructionToast;
41 
42     private boolean mShowLoadingState;
43     private Drawable mLoadingStatePlaceholder;
44     private final Rect mLoadingStateBounds = new Rect();
45 
DeepShortcutTextView(Context context)46     public DeepShortcutTextView(Context context) {
47         this(context, null, 0);
48     }
49 
DeepShortcutTextView(Context context, AttributeSet attrs)50     public DeepShortcutTextView(Context context, AttributeSet attrs) {
51         this(context, attrs, 0);
52     }
53 
DeepShortcutTextView(Context context, AttributeSet attrs, int defStyle)54     public DeepShortcutTextView(Context context, AttributeSet attrs, int defStyle) {
55         super(context, attrs, defStyle);
56 
57         Resources resources = getResources();
58         mDragHandleWidth = resources.getDimensionPixelSize(R.dimen.popup_padding_end)
59                 + resources.getDimensionPixelSize(R.dimen.deep_shortcut_drag_handle_size)
60                 + resources.getDimensionPixelSize(R.dimen.deep_shortcut_drawable_padding) / 2;
61         showLoadingState(true);
62     }
63 
64     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)65     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
66         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
67 
68         mDragHandleBounds.set(0, 0, mDragHandleWidth, getMeasuredHeight());
69         if (!Utilities.isRtl(getResources())) {
70             mDragHandleBounds.offset(getMeasuredWidth() - mDragHandleBounds.width(), 0);
71         }
72 
73         setLoadingBounds();
74     }
75 
setLoadingBounds()76     private void setLoadingBounds() {
77         if (mLoadingStatePlaceholder == null) {
78             return;
79         }
80         mLoadingStateBounds.set(
81                 0,
82                 0,
83                 getMeasuredWidth() - mDragHandleWidth - getPaddingStart(),
84                 mLoadingStatePlaceholder.getIntrinsicHeight());
85         mLoadingStateBounds.offset(
86                 Utilities.isRtl(getResources()) ? mDragHandleWidth : getPaddingStart(),
87                 (int) ((getMeasuredHeight() - mLoadingStatePlaceholder.getIntrinsicHeight())
88                         / 2.0f)
89         );
90         mLoadingStatePlaceholder.setBounds(mLoadingStateBounds);
91     }
92 
93     @Override
applyCompoundDrawables(Drawable icon)94     protected void applyCompoundDrawables(Drawable icon) {
95         // The icon is drawn in a separate view.
96     }
97 
98     @Override
setText(CharSequence text, BufferType type)99     public void setText(CharSequence text, BufferType type) {
100         super.setText(text, type);
101 
102         if (!TextUtils.isEmpty(text)) {
103             showLoadingState(false);
104         }
105     }
106 
107     @Override
shouldIgnoreTouchDown(float x, float y)108     protected boolean shouldIgnoreTouchDown(float x, float y) {
109         // Show toast if user touches the drag handle (long clicks still start the drag).
110         mShowInstructionToast = mDragHandleBounds.contains((int) x, (int) y);
111 
112         // assume the whole view as clickable
113         return false;
114     }
115 
116     @Override
performClick()117     public boolean performClick() {
118         if (mShowInstructionToast) {
119             showToast();
120             return true;
121         }
122         return super.performClick();
123     }
124 
125     @Override
onDraw(Canvas canvas)126     public void onDraw(Canvas canvas) {
127         if (!mShowLoadingState) {
128             super.onDraw(canvas);
129             return;
130         }
131 
132         mLoadingStatePlaceholder.draw(canvas);
133     }
134 
135     @Override
drawDotIfNecessary(Canvas canvas)136     protected void drawDotIfNecessary(Canvas canvas) {
137         // This view (with the text label to the side of the icon) is not designed for a dot to be
138         // drawn on top of it, so never draw one even if a notification for this shortcut exists.
139     }
140 
showLoadingState(boolean loading)141     private void showLoadingState(boolean loading) {
142         if (loading == mShowLoadingState) {
143             return;
144         }
145 
146         mShowLoadingState = loading;
147 
148         if (loading) {
149             mLoadingStatePlaceholder = getContext().getDrawable(
150                     R.drawable.deep_shortcuts_text_placeholder);
151             setLoadingBounds();
152         } else {
153             mLoadingStatePlaceholder = null;
154         }
155 
156         invalidate();
157     }
158 
showToast()159     private void showToast() {
160         if (mInstructionToast != null) {
161             mInstructionToast.cancel();
162         }
163         CharSequence msg = Utilities.wrapForTts(
164                 getContext().getText(R.string.long_press_shortcut_to_add),
165                 getContext().getString(R.string.long_accessible_way_to_add_shortcut));
166         mInstructionToast = Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT);
167         mInstructionToast.show();
168     }
169 }
170