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