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_OFFSET; 20 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.ResolveInfo; 25 import android.graphics.Rect; 26 import android.graphics.RectF; 27 import android.text.TextUtils; 28 import android.util.ArrayMap; 29 import android.util.AttributeSet; 30 import android.view.MotionEvent; 31 import android.view.View; 32 import android.view.View.OnClickListener; 33 import android.view.View.OnLongClickListener; 34 import android.widget.Toast; 35 36 import com.android.launcher3.Launcher; 37 import com.android.launcher3.R; 38 import com.android.launcher3.Utilities; 39 import com.android.launcher3.config.FeatureFlags; 40 import com.android.launcher3.popup.ArrowPopup; 41 import com.android.launcher3.shortcuts.DeepShortcutView; 42 import com.android.launcher3.userevent.nano.LauncherLogProto.Action; 43 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; 44 import com.android.launcher3.widget.WidgetsFullSheet; 45 46 import java.util.ArrayList; 47 import java.util.List; 48 49 import androidx.annotation.VisibleForTesting; 50 51 /** 52 * Popup shown on long pressing an empty space in launcher 53 */ 54 public class OptionsPopupView extends ArrowPopup 55 implements OnClickListener, OnLongClickListener { 56 57 private final ArrayMap<View, OptionItem> mItemMap = new ArrayMap<>(); 58 private RectF mTargetRect; 59 OptionsPopupView(Context context, AttributeSet attrs)60 public OptionsPopupView(Context context, AttributeSet attrs) { 61 this(context, attrs, 0); 62 } 63 OptionsPopupView(Context context, AttributeSet attrs, int defStyleAttr)64 public OptionsPopupView(Context context, AttributeSet attrs, int defStyleAttr) { 65 super(context, attrs, defStyleAttr); 66 } 67 68 @Override onClick(View view)69 public void onClick(View view) { 70 handleViewClick(view, Action.Touch.TAP); 71 } 72 73 @Override onLongClick(View view)74 public boolean onLongClick(View view) { 75 return handleViewClick(view, Action.Touch.LONGPRESS); 76 } 77 handleViewClick(View view, int action)78 private boolean handleViewClick(View view, int action) { 79 OptionItem item = mItemMap.get(view); 80 if (item == null) { 81 return false; 82 } 83 if (item.mControlTypeForLog > 0) { 84 logTap(action, item.mControlTypeForLog); 85 } 86 if (item.mClickListener.onLongClick(view)) { 87 close(true); 88 return true; 89 } 90 return false; 91 } 92 logTap(int action, int controlType)93 private void logTap(int action, int controlType) { 94 mLauncher.getUserEventDispatcher().logActionOnControl(action, controlType); 95 } 96 97 @Override onControllerInterceptTouchEvent(MotionEvent ev)98 public boolean onControllerInterceptTouchEvent(MotionEvent ev) { 99 if (ev.getAction() != MotionEvent.ACTION_DOWN) { 100 return false; 101 } 102 if (getPopupContainer().isEventOverView(this, ev)) { 103 return false; 104 } 105 close(true); 106 return true; 107 } 108 109 @Override logActionCommand(int command)110 public void logActionCommand(int command) { 111 // TODO: 112 } 113 114 @Override isOfType(int type)115 protected boolean isOfType(int type) { 116 return (type & TYPE_OPTIONS_POPUP) != 0; 117 } 118 119 @Override getTargetObjectLocation(Rect outPos)120 protected void getTargetObjectLocation(Rect outPos) { 121 mTargetRect.roundOut(outPos); 122 } 123 show(Launcher launcher, RectF targetRect, List<OptionItem> items)124 public static void show(Launcher launcher, RectF targetRect, List<OptionItem> items) { 125 OptionsPopupView popup = (OptionsPopupView) launcher.getLayoutInflater() 126 .inflate(R.layout.longpress_options_menu, launcher.getDragLayer(), false); 127 popup.mTargetRect = targetRect; 128 129 for (OptionItem item : items) { 130 DeepShortcutView view = popup.inflateAndAdd(R.layout.system_shortcut, popup); 131 view.getIconView().setBackgroundResource(item.mIconRes); 132 view.getBubbleText().setText(item.mLabelRes); 133 view.setDividerVisibility(View.INVISIBLE); 134 view.setOnClickListener(popup); 135 view.setOnLongClickListener(popup); 136 popup.mItemMap.put(view, item); 137 } 138 popup.reorderAndShow(popup.getChildCount()); 139 } 140 141 @VisibleForTesting getOptionsPopup(Launcher launcher)142 public static ArrowPopup getOptionsPopup(Launcher launcher) { 143 return launcher.findViewById(R.id.deep_shortcuts_container); 144 } 145 showDefaultOptions(Launcher launcher, float x, float y)146 public static void showDefaultOptions(Launcher launcher, float x, float y) { 147 float halfSize = launcher.getResources().getDimension(R.dimen.options_menu_thumb_size) / 2; 148 if (x < 0 || y < 0) { 149 x = launcher.getDragLayer().getWidth() / 2; 150 y = launcher.getDragLayer().getHeight() / 2; 151 } 152 RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize); 153 154 ArrayList<OptionItem> options = new ArrayList<>(); 155 int resString = Utilities.existsStyleWallpapers(launcher) ? 156 R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text; 157 int resDrawable = Utilities.existsStyleWallpapers(launcher) ? 158 R.drawable.ic_palette : R.drawable.ic_wallpaper; 159 options.add(new OptionItem(resString, resDrawable, 160 ControlType.WALLPAPER_BUTTON, OptionsPopupView::startWallpaperPicker)); 161 if (!FeatureFlags.GO_DISABLE_WIDGETS) { 162 options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget, 163 ControlType.WIDGETS_BUTTON, OptionsPopupView::onWidgetsClicked)); 164 } 165 options.add(new OptionItem(R.string.settings_button_text, R.drawable.ic_setting, 166 ControlType.SETTINGS_BUTTON, OptionsPopupView::startSettings)); 167 168 show(launcher, target, options); 169 } 170 onWidgetsClicked(View view)171 public static boolean onWidgetsClicked(View view) { 172 return openWidgets(Launcher.getLauncher(view.getContext())); 173 } 174 openWidgets(Launcher launcher)175 public static boolean openWidgets(Launcher launcher) { 176 if (launcher.getPackageManager().isSafeMode()) { 177 Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show(); 178 return false; 179 } else { 180 WidgetsFullSheet.show(launcher, true /* animated */); 181 return true; 182 } 183 } 184 startSettings(View view)185 public static boolean startSettings(View view) { 186 Launcher launcher = Launcher.getLauncher(view.getContext()); 187 launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES) 188 .setPackage(launcher.getPackageName()) 189 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); 190 return true; 191 } 192 193 /** 194 * Event handler for the wallpaper picker button that appears after a long press 195 * on the home screen. 196 */ startWallpaperPicker(View v)197 public static boolean startWallpaperPicker(View v) { 198 Launcher launcher = Launcher.getLauncher(v.getContext()); 199 if (!Utilities.isWallpaperAllowed(launcher)) { 200 Toast.makeText(launcher, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show(); 201 return false; 202 } 203 Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER) 204 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) 205 .putExtra(EXTRA_WALLPAPER_OFFSET, 206 launcher.getWorkspace().getWallpaperOffsetForCenterPage()); 207 if (!Utilities.existsStyleWallpapers(launcher)) { 208 intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only"); 209 } else { 210 intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "focus_wallpaper"); 211 } 212 String pickerPackage = launcher.getString(R.string.wallpaper_picker_package); 213 if (!TextUtils.isEmpty(pickerPackage)) { 214 intent.setPackage(pickerPackage); 215 } 216 return launcher.startActivitySafely(v, intent, null, null); 217 } 218 219 public static class OptionItem { 220 221 private final int mLabelRes; 222 private final int mIconRes; 223 private final int mControlTypeForLog; 224 private final OnLongClickListener mClickListener; 225 OptionItem(int labelRes, int iconRes, int controlTypeForLog, OnLongClickListener clickListener)226 public OptionItem(int labelRes, int iconRes, int controlTypeForLog, 227 OnLongClickListener clickListener) { 228 mLabelRes = labelRes; 229 mIconRes = iconRes; 230 mControlTypeForLog = controlTypeForLog; 231 mClickListener = clickListener; 232 } 233 } 234 } 235