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.Context; 22 import android.content.Intent; 23 import android.graphics.Rect; 24 import android.graphics.RectF; 25 import android.text.TextUtils; 26 import android.util.ArrayMap; 27 import android.util.AttributeSet; 28 import android.view.MotionEvent; 29 import android.view.View; 30 import android.view.View.OnClickListener; 31 import android.view.View.OnLongClickListener; 32 import android.widget.Toast; 33 34 import androidx.annotation.Nullable; 35 import androidx.annotation.VisibleForTesting; 36 37 import com.android.launcher3.Launcher; 38 import com.android.launcher3.R; 39 import com.android.launcher3.Utilities; 40 import com.android.launcher3.config.FeatureFlags; 41 import com.android.launcher3.popup.ArrowPopup; 42 import com.android.launcher3.shortcuts.DeepShortcutView; 43 import com.android.launcher3.userevent.nano.LauncherLogProto.Action; 44 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; 45 import com.android.launcher3.widget.WidgetsFullSheet; 46 47 import java.util.ArrayList; 48 import java.util.List; 49 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())) != null; 173 } 174 175 /** Returns WidgetsFullSheet that was opened, or null if nothing was opened. */ 176 @Nullable openWidgets(Launcher launcher)177 public static WidgetsFullSheet openWidgets(Launcher launcher) { 178 if (launcher.getPackageManager().isSafeMode()) { 179 Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show(); 180 return null; 181 } else { 182 return WidgetsFullSheet.show(launcher, true /* animated */); 183 } 184 } 185 startSettings(View view)186 public static boolean startSettings(View view) { 187 Launcher launcher = Launcher.getLauncher(view.getContext()); 188 launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES) 189 .setPackage(launcher.getPackageName()) 190 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); 191 return true; 192 } 193 194 /** 195 * Event handler for the wallpaper picker button that appears after a long press 196 * on the home screen. 197 */ startWallpaperPicker(View v)198 public static boolean startWallpaperPicker(View v) { 199 Launcher launcher = Launcher.getLauncher(v.getContext()); 200 if (!Utilities.isWallpaperAllowed(launcher)) { 201 Toast.makeText(launcher, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show(); 202 return false; 203 } 204 Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER) 205 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) 206 .putExtra(EXTRA_WALLPAPER_OFFSET, 207 launcher.getWorkspace().getWallpaperOffsetForCenterPage()); 208 if (!Utilities.existsStyleWallpapers(launcher)) { 209 intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only"); 210 } else { 211 intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "focus_wallpaper"); 212 } 213 String pickerPackage = launcher.getString(R.string.wallpaper_picker_package); 214 if (!TextUtils.isEmpty(pickerPackage)) { 215 intent.setPackage(pickerPackage); 216 } 217 return launcher.startActivitySafely(v, intent, null, null); 218 } 219 220 public static class OptionItem { 221 222 private final int mLabelRes; 223 private final int mIconRes; 224 private final int mControlTypeForLog; 225 private final OnLongClickListener mClickListener; 226 OptionItem(int labelRes, int iconRes, int controlTypeForLog, OnLongClickListener clickListener)227 public OptionItem(int labelRes, int iconRes, int controlTypeForLog, 228 OnLongClickListener clickListener) { 229 mLabelRes = labelRes; 230 mIconRes = iconRes; 231 mControlTypeForLog = controlTypeForLog; 232 mClickListener = clickListener; 233 } 234 } 235 } 236