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