• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.views;
17 
18 import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR;
19 import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_LAUNCH_SOURCE;
20 import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
21 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
22 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS;
23 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS;
24 
25 import android.content.Context;
26 import android.content.Intent;
27 import android.graphics.Rect;
28 import android.graphics.RectF;
29 import android.graphics.drawable.Drawable;
30 import android.text.TextUtils;
31 import android.util.ArrayMap;
32 import android.util.AttributeSet;
33 import android.view.MotionEvent;
34 import android.view.View;
35 import android.view.View.OnClickListener;
36 import android.view.View.OnLongClickListener;
37 import android.widget.Toast;
38 
39 import androidx.annotation.Nullable;
40 import androidx.annotation.VisibleForTesting;
41 import androidx.core.content.ContextCompat;
42 
43 import com.android.launcher3.Launcher;
44 import com.android.launcher3.LauncherSettings;
45 import com.android.launcher3.R;
46 import com.android.launcher3.Utilities;
47 import com.android.launcher3.logging.StatsLogManager.EventEnum;
48 import com.android.launcher3.model.WidgetsModel;
49 import com.android.launcher3.model.data.WorkspaceItemInfo;
50 import com.android.launcher3.popup.ArrowPopup;
51 import com.android.launcher3.shortcuts.DeepShortcutView;
52 import com.android.launcher3.testing.TestLogging;
53 import com.android.launcher3.testing.TestProtocol;
54 import com.android.launcher3.widget.picker.WidgetsFullSheet;
55 
56 import java.util.ArrayList;
57 import java.util.List;
58 
59 /**
60  * Popup shown on long pressing an empty space in launcher
61  */
62 public class OptionsPopupView extends ArrowPopup
63         implements OnClickListener, OnLongClickListener {
64 
65     private final ArrayMap<View, OptionItem> mItemMap = new ArrayMap<>();
66     private RectF mTargetRect;
67     private boolean mShouldAddArrow;
68 
OptionsPopupView(Context context, AttributeSet attrs)69     public OptionsPopupView(Context context, AttributeSet attrs) {
70         this(context, attrs, 0);
71     }
72 
OptionsPopupView(Context context, AttributeSet attrs, int defStyleAttr)73     public OptionsPopupView(Context context, AttributeSet attrs, int defStyleAttr) {
74         super(context, attrs, defStyleAttr);
75     }
76 
77     @Override
onClick(View view)78     public void onClick(View view) {
79         handleViewClick(view);
80     }
81 
82     @Override
onLongClick(View view)83     public boolean onLongClick(View view) {
84         return handleViewClick(view);
85     }
86 
handleViewClick(View view)87     private boolean handleViewClick(View view) {
88         OptionItem item = mItemMap.get(view);
89         if (item == null) {
90             return false;
91         }
92         if (item.eventId.getId() > 0) {
93             mLauncher.getStatsLogManager().logger().log(item.eventId);
94         }
95         if (item.clickListener.onLongClick(view)) {
96             close(true);
97             return true;
98         }
99         return false;
100     }
101 
102     @Override
onControllerInterceptTouchEvent(MotionEvent ev)103     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
104         if (ev.getAction() != MotionEvent.ACTION_DOWN) {
105             return false;
106         }
107         if (getPopupContainer().isEventOverView(this, ev)) {
108             return false;
109         }
110         close(true);
111         return true;
112     }
113 
114     @Override
isOfType(int type)115     protected boolean isOfType(int type) {
116         return (type & TYPE_OPTIONS_POPUP) != 0;
117     }
118 
setShouldAddArrow(boolean shouldAddArrow)119     public void setShouldAddArrow(boolean shouldAddArrow) {
120         mShouldAddArrow = shouldAddArrow;
121     }
122 
123     @Override
shouldAddArrow()124     protected boolean shouldAddArrow() {
125         return mShouldAddArrow;
126     }
127 
128     @Override
getTargetObjectLocation(Rect outPos)129     protected void getTargetObjectLocation(Rect outPos) {
130         mTargetRect.roundOut(outPos);
131     }
132 
show( Launcher launcher, RectF targetRect, List<OptionItem> items, boolean shouldAddArrow)133     public static OptionsPopupView show(
134             Launcher launcher, RectF targetRect, List<OptionItem> items, boolean shouldAddArrow) {
135         return show(launcher, targetRect, items, shouldAddArrow, 0 /* width */);
136     }
137 
show( Launcher launcher, RectF targetRect, List<OptionItem> items, boolean shouldAddArrow, int width)138     public static OptionsPopupView show(
139             Launcher launcher, RectF targetRect, List<OptionItem> items, boolean shouldAddArrow,
140             int width) {
141         OptionsPopupView popup = (OptionsPopupView) launcher.getLayoutInflater()
142                 .inflate(R.layout.longpress_options_menu, launcher.getDragLayer(), false);
143         popup.mTargetRect = targetRect;
144         popup.setShouldAddArrow(shouldAddArrow);
145 
146         for (OptionItem item : items) {
147             DeepShortcutView view =
148                     (DeepShortcutView) popup.inflateAndAdd(R.layout.system_shortcut, popup);
149             if (width > 0) {
150                 view.getLayoutParams().width = width;
151             }
152             view.getIconView().setBackgroundDrawable(item.icon);
153             view.getBubbleText().setText(item.label);
154             view.setOnClickListener(popup);
155             view.setOnLongClickListener(popup);
156             popup.mItemMap.put(view, item);
157         }
158 
159         popup.addPreDrawForColorExtraction(launcher);
160         popup.show();
161         return popup;
162     }
163 
164     @Override
getChildrenForColorExtraction()165     protected List<View> getChildrenForColorExtraction() {
166         int childCount = getChildCount();
167         ArrayList<View> children = new ArrayList<>(childCount);
168         for (int i = 0; i < childCount; ++i) {
169             children.add(getChildAt(i));
170         }
171         return children;
172     }
173 
174     @VisibleForTesting
getOptionsPopup(Launcher launcher)175     public static ArrowPopup getOptionsPopup(Launcher launcher) {
176         return launcher.findViewById(R.id.popup_container);
177     }
178 
showDefaultOptions(Launcher launcher, float x, float y)179     public static void showDefaultOptions(Launcher launcher, float x, float y) {
180         float halfSize = launcher.getResources().getDimension(R.dimen.options_menu_thumb_size) / 2;
181         if (x < 0 || y < 0) {
182             x = launcher.getDragLayer().getWidth() / 2;
183             y = launcher.getDragLayer().getHeight() / 2;
184         }
185         RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
186         show(launcher, target, getOptions(launcher), false);
187     }
188 
189     /**
190      * Returns the list of supported actions
191      */
getOptions(Launcher launcher)192     public static ArrayList<OptionItem> getOptions(Launcher launcher) {
193         ArrayList<OptionItem> options = new ArrayList<>();
194         options.add(new OptionItem(launcher,
195                 R.string.settings_button_text,
196                 R.drawable.ic_setting,
197                 LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS,
198                 OptionsPopupView::startSettings));
199         if (!WidgetsModel.GO_DISABLE_WIDGETS) {
200             options.add(new OptionItem(launcher,
201                     R.string.widget_button_text,
202                     R.drawable.ic_widget,
203                     LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS,
204                     OptionsPopupView::onWidgetsClicked));
205         }
206         int resString = Utilities.existsStyleWallpapers(launcher) ?
207                 R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text;
208         int resDrawable = Utilities.existsStyleWallpapers(launcher) ?
209                 R.drawable.ic_palette : R.drawable.ic_wallpaper;
210         options.add(new OptionItem(launcher,
211                 resString,
212                 resDrawable,
213                 IGNORE,
214                 OptionsPopupView::startWallpaperPicker));
215         return options;
216     }
217 
onWidgetsClicked(View view)218     private static boolean onWidgetsClicked(View view) {
219         return openWidgets(Launcher.getLauncher(view.getContext())) != null;
220     }
221 
222     /** Returns WidgetsFullSheet that was opened, or null if nothing was opened. */
223     @Nullable
openWidgets(Launcher launcher)224     public static WidgetsFullSheet openWidgets(Launcher launcher) {
225         if (launcher.getPackageManager().isSafeMode()) {
226             Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
227             return null;
228         } else {
229             return WidgetsFullSheet.show(launcher, true /* animated */);
230         }
231     }
232 
startSettings(View view)233     private static boolean startSettings(View view) {
234         TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: startSettings");
235         Launcher launcher = Launcher.getLauncher(view.getContext());
236         launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
237                 .setPackage(launcher.getPackageName())
238                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
239         return true;
240     }
241 
242     /**
243      * Event handler for the wallpaper picker button that appears after a long press
244      * on the home screen.
245      */
startWallpaperPicker(View v)246     private static boolean startWallpaperPicker(View v) {
247         Launcher launcher = Launcher.getLauncher(v.getContext());
248         if (!Utilities.isWallpaperAllowed(launcher)) {
249             Toast.makeText(launcher, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
250             return false;
251         }
252         Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
253                 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
254                 .putExtra(EXTRA_WALLPAPER_OFFSET,
255                         launcher.getWorkspace().getWallpaperOffsetForCenterPage())
256                 .putExtra(EXTRA_WALLPAPER_LAUNCH_SOURCE, "app_launched_launcher");
257         if (!Utilities.existsStyleWallpapers(launcher)) {
258             intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only");
259         } else {
260             intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "focus_wallpaper");
261         }
262         String pickerPackage = launcher.getString(R.string.wallpaper_picker_package);
263         if (!TextUtils.isEmpty(pickerPackage)) {
264             intent.setPackage(pickerPackage);
265         }
266         return launcher.startActivitySafely(v, intent, placeholderInfo(intent));
267     }
268 
placeholderInfo(Intent intent)269     static WorkspaceItemInfo placeholderInfo(Intent intent) {
270         WorkspaceItemInfo placeholderInfo = new WorkspaceItemInfo();
271         placeholderInfo.intent = intent;
272         placeholderInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
273         placeholderInfo.container = LauncherSettings.Favorites.CONTAINER_SETTINGS;
274         return placeholderInfo;
275     }
276 
277     public static class OptionItem {
278 
279         // Used to create AccessibilityNodeInfo in AccessibilityActionsView.java.
280         public final int labelRes;
281 
282         public final CharSequence label;
283         public final Drawable icon;
284         public final EventEnum eventId;
285         public final OnLongClickListener clickListener;
286 
OptionItem(Context context, int labelRes, int iconRes, EventEnum eventId, OnLongClickListener clickListener)287         public OptionItem(Context context, int labelRes, int iconRes, EventEnum eventId,
288                           OnLongClickListener clickListener) {
289             this.labelRes = labelRes;
290             this.label = context.getText(labelRes);
291             this.icon = ContextCompat.getDrawable(context, iconRes);
292             this.eventId = eventId;
293             this.clickListener = clickListener;
294         }
295 
OptionItem(CharSequence label, Drawable icon, EventEnum eventId, OnLongClickListener clickListener)296         public OptionItem(CharSequence label, Drawable icon, EventEnum eventId,
297                           OnLongClickListener clickListener) {
298             this.labelRes = 0;
299             this.label = label;
300             this.icon = icon;
301             this.eventId = eventId;
302             this.clickListener = clickListener;
303         }
304     }
305 }
306