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