1/* 2 * Copyright (C) 2016 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 17package com.android.launcher3; 18 19import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED; 20import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED; 21import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; 22 23import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; 24import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; 25 26import android.annotation.SuppressLint; 27import android.content.Context; 28import android.support.annotation.IntDef; 29import android.util.AttributeSet; 30import android.util.Pair; 31import android.view.MotionEvent; 32import android.view.View; 33import android.widget.LinearLayout; 34 35import com.android.launcher3.userevent.nano.LauncherLogProto.Action; 36import com.android.launcher3.util.TouchController; 37import com.android.launcher3.views.BaseDragLayer; 38 39import java.lang.annotation.Retention; 40import java.lang.annotation.RetentionPolicy; 41 42/** 43 * Base class for a View which shows a floating UI on top of the launcher UI. 44 */ 45public abstract class AbstractFloatingView extends LinearLayout implements TouchController { 46 47 @IntDef(flag = true, value = { 48 TYPE_FOLDER, 49 TYPE_ACTION_POPUP, 50 TYPE_WIDGETS_BOTTOM_SHEET, 51 TYPE_WIDGET_RESIZE_FRAME, 52 TYPE_WIDGETS_FULL_SHEET, 53 TYPE_ON_BOARD_POPUP, 54 TYPE_DISCOVERY_BOUNCE, 55 56 TYPE_QUICKSTEP_PREVIEW, 57 TYPE_TASK_MENU, 58 TYPE_OPTIONS_POPUP 59 }) 60 @Retention(RetentionPolicy.SOURCE) 61 public @interface FloatingViewType {} 62 public static final int TYPE_FOLDER = 1 << 0; 63 public static final int TYPE_ACTION_POPUP = 1 << 1; 64 public static final int TYPE_WIDGETS_BOTTOM_SHEET = 1 << 2; 65 public static final int TYPE_WIDGET_RESIZE_FRAME = 1 << 3; 66 public static final int TYPE_WIDGETS_FULL_SHEET = 1 << 4; 67 public static final int TYPE_ON_BOARD_POPUP = 1 << 5; 68 public static final int TYPE_DISCOVERY_BOUNCE = 1 << 6; 69 70 // Popups related to quickstep UI 71 public static final int TYPE_QUICKSTEP_PREVIEW = 1 << 6; 72 public static final int TYPE_TASK_MENU = 1 << 7; 73 public static final int TYPE_OPTIONS_POPUP = 1 << 8; 74 75 public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP 76 | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET 77 | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU 78 | TYPE_OPTIONS_POPUP; 79 80 // Type of popups which should be kept open during launcher rebind 81 public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET 82 | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE; 83 84 // Usually we show the back button when a floating view is open. Instead, hide for these types. 85 public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE; 86 87 public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE; 88 89 protected boolean mIsOpen; 90 91 public AbstractFloatingView(Context context, AttributeSet attrs) { 92 super(context, attrs); 93 } 94 95 public AbstractFloatingView(Context context, AttributeSet attrs, int defStyleAttr) { 96 super(context, attrs, defStyleAttr); 97 } 98 99 /** 100 * We need to handle touch events to prevent them from falling through to the workspace below. 101 */ 102 @SuppressLint("ClickableViewAccessibility") 103 @Override 104 public boolean onTouchEvent(MotionEvent ev) { 105 return true; 106 } 107 108 public final void close(boolean animate) { 109 animate &= !Utilities.isPowerSaverPreventingAnimation(getContext()); 110 if (mIsOpen) { 111 BaseActivity.fromContext(getContext()).getUserEventDispatcher() 112 .resetElapsedContainerMillis("container closed"); 113 } 114 handleClose(animate); 115 mIsOpen = false; 116 } 117 118 protected abstract void handleClose(boolean animate); 119 120 public abstract void logActionCommand(int command); 121 122 public final boolean isOpen() { 123 return mIsOpen; 124 } 125 126 protected void onWidgetsBound() { 127 } 128 129 protected abstract boolean isOfType(@FloatingViewType int type); 130 131 /** @return Whether the back is consumed. If false, Launcher will handle the back as well. */ 132 public boolean onBackPressed() { 133 logActionCommand(Action.Command.BACK); 134 close(true); 135 return true; 136 } 137 138 @Override 139 public boolean onControllerTouchEvent(MotionEvent ev) { 140 return false; 141 } 142 143 protected void announceAccessibilityChanges() { 144 Pair<View, String> targetInfo = getAccessibilityTarget(); 145 if (targetInfo == null || !isAccessibilityEnabled(getContext())) { 146 return; 147 } 148 sendCustomAccessibilityEvent( 149 targetInfo.first, TYPE_WINDOW_STATE_CHANGED, targetInfo.second); 150 151 if (mIsOpen) { 152 sendAccessibilityEvent(TYPE_VIEW_FOCUSED); 153 } 154 BaseDraggingActivity.fromContext(getContext()).getDragLayer() 155 .sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED); 156 } 157 158 protected Pair<View, String> getAccessibilityTarget() { 159 return null; 160 } 161 162 protected static <T extends AbstractFloatingView> T getOpenView( 163 BaseDraggingActivity activity, @FloatingViewType int type) { 164 BaseDragLayer dragLayer = activity.getDragLayer(); 165 // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer, 166 // and will be one of the last views. 167 for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) { 168 View child = dragLayer.getChildAt(i); 169 if (child instanceof AbstractFloatingView) { 170 AbstractFloatingView view = (AbstractFloatingView) child; 171 if (view.isOfType(type) && view.isOpen()) { 172 return (T) view; 173 } 174 } 175 } 176 return null; 177 } 178 179 public static void closeOpenContainer(BaseDraggingActivity activity, 180 @FloatingViewType int type) { 181 AbstractFloatingView view = getOpenView(activity, type); 182 if (view != null) { 183 view.close(true); 184 } 185 } 186 187 public static void closeOpenViews(BaseDraggingActivity activity, boolean animate, 188 @FloatingViewType int type) { 189 BaseDragLayer dragLayer = activity.getDragLayer(); 190 // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer, 191 // and will be one of the last views. 192 for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) { 193 View child = dragLayer.getChildAt(i); 194 if (child instanceof AbstractFloatingView) { 195 AbstractFloatingView abs = (AbstractFloatingView) child; 196 if (abs.isOfType(type)) { 197 abs.close(animate); 198 } 199 } 200 } 201 } 202 203 public static void closeAllOpenViews(BaseDraggingActivity activity, boolean animate) { 204 closeOpenViews(activity, animate, TYPE_ALL); 205 activity.finishAutoCancelActionMode(); 206 } 207 208 public static void closeAllOpenViews(BaseDraggingActivity activity) { 209 closeAllOpenViews(activity, true); 210 } 211 212 public static AbstractFloatingView getTopOpenView(BaseDraggingActivity activity) { 213 return getTopOpenViewWithType(activity, TYPE_ALL); 214 } 215 216 public static AbstractFloatingView getTopOpenViewWithType(BaseDraggingActivity activity, 217 @FloatingViewType int type) { 218 return getOpenView(activity, type); 219 } 220} 221