• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.launcher3.widget;
17 
18 import android.content.Context;
19 import android.graphics.Point;
20 import android.graphics.Rect;
21 import android.util.AttributeSet;
22 import android.view.View;
23 import android.view.View.OnClickListener;
24 import android.view.View.OnLongClickListener;
25 import android.widget.Toast;
26 
27 import androidx.annotation.GuardedBy;
28 import androidx.annotation.Nullable;
29 import androidx.core.view.ViewCompat;
30 
31 import com.android.launcher3.DeviceProfile;
32 import com.android.launcher3.DragSource;
33 import com.android.launcher3.DropTarget.DragObject;
34 import com.android.launcher3.Insettable;
35 import com.android.launcher3.Launcher;
36 import com.android.launcher3.R;
37 import com.android.launcher3.Utilities;
38 import com.android.launcher3.dragndrop.DragOptions;
39 import com.android.launcher3.popup.PopupDataProvider;
40 import com.android.launcher3.testing.TestLogging;
41 import com.android.launcher3.testing.TestProtocol;
42 import com.android.launcher3.touch.ItemLongClickListener;
43 import com.android.launcher3.util.SystemUiController;
44 import com.android.launcher3.util.Themes;
45 import com.android.launcher3.views.AbstractSlideInView;
46 import com.android.launcher3.views.ArrowTipView;
47 
48 /**
49  * Base class for various widgets popup
50  */
51 public abstract class BaseWidgetSheet extends AbstractSlideInView<Launcher>
52         implements OnClickListener, OnLongClickListener, DragSource,
53         PopupDataProvider.PopupDataChangeListener, Insettable {
54 
55     protected static final String KEY_WIDGETS_EDUCATION_TIP_SEEN =
56             "launcher.widgets_education_tip_seen";
57     protected final Rect mInsets = new Rect();
58 
59     /* Touch handling related member variables. */
60     private Toast mWidgetInstructionToast;
61 
BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr)62     public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
63         super(context, attrs, defStyleAttr);
64     }
65 
getScrimColor(Context context)66     protected int getScrimColor(Context context) {
67         return context.getResources().getColor(R.color.widgets_picker_scrim);
68     }
69 
70     @Override
onAttachedToWindow()71     protected void onAttachedToWindow() {
72         super.onAttachedToWindow();
73         mActivityContext.getPopupDataProvider().setChangeListener(this);
74     }
75 
76     @Override
onDetachedFromWindow()77     protected void onDetachedFromWindow() {
78         super.onDetachedFromWindow();
79         mActivityContext.getPopupDataProvider().setChangeListener(null);
80     }
81 
82     @Override
onClick(View v)83     public final void onClick(View v) {
84         Object tag = null;
85         if (v instanceof WidgetCell) {
86             tag = v.getTag();
87         } else if (v.getParent() instanceof WidgetCell) {
88             tag = ((WidgetCell) v.getParent()).getTag();
89         }
90         if (tag instanceof PendingAddShortcutInfo) {
91             mWidgetInstructionToast = showShortcutToast(getContext(), mWidgetInstructionToast);
92         } else {
93             mWidgetInstructionToast = showWidgetToast(getContext(), mWidgetInstructionToast);
94         }
95 
96     }
97 
98     @Override
onLongClick(View v)99     public boolean onLongClick(View v) {
100         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick");
101         v.cancelLongPress();
102         if (!ItemLongClickListener.canStartDrag(mActivityContext)) return false;
103 
104         if (v instanceof WidgetCell) {
105             return beginDraggingWidget((WidgetCell) v);
106         } else if (v.getParent() instanceof WidgetCell) {
107             return beginDraggingWidget((WidgetCell) v.getParent());
108         }
109         return true;
110     }
111 
112     @Override
setInsets(Rect insets)113     public void setInsets(Rect insets) {
114         mInsets.set(insets);
115     }
116 
117 
118     /**
119      * Measures the dimension of this view and its children by taking system insets, navigation bar,
120      * status bar, into account.
121      */
122     @GuardedBy("MainThread")
doMeasure(int widthMeasureSpec, int heightMeasureSpec)123     protected void doMeasure(int widthMeasureSpec, int heightMeasureSpec) {
124         DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
125         int widthUsed;
126         if (mInsets.bottom > 0) {
127             widthUsed = mInsets.left + mInsets.right;
128         } else {
129             Rect padding = deviceProfile.workspacePadding;
130             widthUsed = Math.max(padding.left + padding.right,
131                     2 * (mInsets.left + mInsets.right));
132         }
133 
134         int heightUsed = mInsets.top + deviceProfile.edgeMarginPx;
135         measureChildWithMargins(mContent, widthMeasureSpec,
136                 widthUsed, heightMeasureSpec, heightUsed);
137         setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
138                 MeasureSpec.getSize(heightMeasureSpec));
139     }
140 
beginDraggingWidget(WidgetCell v)141     private boolean beginDraggingWidget(WidgetCell v) {
142         // Get the widget preview as the drag representation
143         WidgetImageView image = v.getWidgetView();
144 
145         // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
146         // we abort the drag.
147         if (image.getDrawable() == null && v.getAppWidgetHostViewPreview() == null) {
148             return false;
149         }
150 
151         PendingItemDragHelper dragHelper = new PendingItemDragHelper(v);
152         dragHelper.setRemoteViewsPreview(v.getRemoteViewsPreview());
153         dragHelper.setAppWidgetHostViewPreview(v.getAppWidgetHostViewPreview());
154 
155         if (image.getDrawable() != null) {
156             int[] loc = new int[2];
157             getPopupContainer().getLocationInDragLayer(image, loc);
158 
159             dragHelper.startDrag(image.getBitmapBounds(), image.getDrawable().getIntrinsicWidth(),
160                     image.getWidth(), new Point(loc[0], loc[1]), this, new DragOptions());
161         } else {
162             View preview = v.getAppWidgetHostViewPreview();
163             int[] loc = new int[2];
164             getPopupContainer().getLocationInDragLayer(preview, loc);
165 
166             Rect r = new Rect(0, 0, preview.getWidth(), preview.getHeight());
167             dragHelper.startDrag(r, preview.getMeasuredWidth(), preview.getMeasuredWidth(),
168                     new Point(loc[0], loc[1]), this, new DragOptions());
169         }
170         close(true);
171         return true;
172     }
173 
174     //
175     // Drag related handling methods that implement {@link DragSource} interface.
176     //
177 
178     @Override
onDropCompleted(View target, DragObject d, boolean success)179     public void onDropCompleted(View target, DragObject d, boolean success) { }
180 
181 
onCloseComplete()182     protected void onCloseComplete() {
183         super.onCloseComplete();
184         clearNavBarColor();
185     }
186 
clearNavBarColor()187     protected void clearNavBarColor() {
188         getSystemUiController().updateUiState(
189                 SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0);
190     }
191 
setupNavBarColor()192     protected void setupNavBarColor() {
193         boolean isSheetDark = Themes.getAttrBoolean(getContext(), R.attr.isMainColorDark);
194         getSystemUiController().updateUiState(
195                 SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET,
196                 isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
197     }
198 
getSystemUiController()199     protected SystemUiController getSystemUiController() {
200         return mActivityContext.getSystemUiController();
201     }
202 
203     /**
204      * Show Widget tap toast prompting user to drag instead
205      */
showWidgetToast(Context context, Toast toast)206     public static Toast showWidgetToast(Context context, Toast toast) {
207         // Let the user know that they have to long press to add a widget
208         if (toast != null) {
209             toast.cancel();
210         }
211 
212         CharSequence msg = Utilities.wrapForTts(
213                 context.getText(R.string.long_press_widget_to_add),
214                 context.getString(R.string.long_accessible_way_to_add));
215         toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
216         toast.show();
217         return toast;
218     }
219 
220     /**
221      * Show shortcut tap toast prompting user to drag instead.
222      */
showShortcutToast(Context context, Toast toast)223     private static Toast showShortcutToast(Context context, Toast toast) {
224         // Let the user know that they have to long press to add a widget
225         if (toast != null) {
226             toast.cancel();
227         }
228 
229         CharSequence msg = Utilities.wrapForTts(
230                 context.getText(R.string.long_press_shortcut_to_add),
231                 context.getString(R.string.long_accessible_way_to_add_shortcut));
232         toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
233         toast.show();
234         return toast;
235     }
236 
237     /** Shows education tip on top center of {@code view} if view is laid out. */
238     @Nullable
showEducationTipOnViewIfPossible(@ullable View view)239     protected ArrowTipView showEducationTipOnViewIfPossible(@Nullable View view) {
240         if (view == null || !ViewCompat.isLaidOut(view)) {
241             return null;
242         }
243         int[] coords = new int[2];
244         view.getLocationOnScreen(coords);
245         ArrowTipView arrowTipView =
246                 new ArrowTipView(mActivityContext,  /* isPointingUp= */ false).showAtLocation(
247                         getContext().getString(R.string.long_press_widget_to_add),
248                         /* arrowXCoord= */coords[0] + view.getWidth() / 2,
249                         /* yCoord= */coords[1]);
250         if (arrowTipView != null) {
251             mActivityContext.getSharedPrefs().edit()
252                     .putBoolean(KEY_WIDGETS_EDUCATION_TIP_SEEN, true).apply();
253         }
254         return arrowTipView;
255     }
256 
257     /** Returns {@code true} if tip has previously been shown on any of {@link BaseWidgetSheet}. */
hasSeenEducationTip()258     protected boolean hasSeenEducationTip() {
259         return mActivityContext.getSharedPrefs().getBoolean(KEY_WIDGETS_EDUCATION_TIP_SEEN, false)
260                 || Utilities.IS_RUNNING_IN_TEST_HARNESS;
261     }
262 }
263