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