• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.launcher3;
18 
19 import static android.animation.ValueAnimator.areAnimatorsEnabled;
20 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
21 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
22 
23 import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
24 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
25 
26 import android.annotation.SuppressLint;
27 import android.content.Context;
28 import android.util.AttributeSet;
29 import android.util.Pair;
30 import android.view.MotionEvent;
31 import android.view.View;
32 import android.view.accessibility.AccessibilityNodeInfo;
33 import android.view.animation.Interpolator;
34 import android.widget.LinearLayout;
35 
36 import androidx.annotation.IntDef;
37 
38 import com.android.launcher3.anim.PendingAnimation;
39 import com.android.launcher3.util.TouchController;
40 import com.android.launcher3.views.ActivityContext;
41 import com.android.launcher3.views.BaseDragLayer;
42 
43 import java.lang.annotation.Retention;
44 import java.lang.annotation.RetentionPolicy;
45 
46 /**
47  * Base class for a View which shows a floating UI on top of the launcher UI.
48  */
49 public abstract class AbstractFloatingView extends LinearLayout implements TouchController {
50 
51     @IntDef(flag = true, value = {
52             TYPE_FOLDER,
53             TYPE_ACTION_POPUP,
54             TYPE_WIDGETS_BOTTOM_SHEET,
55             TYPE_WIDGET_RESIZE_FRAME,
56             TYPE_WIDGETS_FULL_SHEET,
57             TYPE_ON_BOARD_POPUP,
58             TYPE_DISCOVERY_BOUNCE,
59             TYPE_SNACKBAR,
60             TYPE_LISTENER,
61             TYPE_ALL_APPS_EDU,
62             TYPE_DRAG_DROP_POPUP,
63             TYPE_TASK_MENU,
64             TYPE_OPTIONS_POPUP,
65             TYPE_ICON_SURFACE,
66             TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP,
67             TYPE_WIDGETS_EDUCATION_DIALOG
68     })
69     @Retention(RetentionPolicy.SOURCE)
70     public @interface FloatingViewType {}
71     public static final int TYPE_FOLDER = 1 << 0;
72     public static final int TYPE_ACTION_POPUP = 1 << 1;
73     public static final int TYPE_WIDGETS_BOTTOM_SHEET = 1 << 2;
74     public static final int TYPE_WIDGET_RESIZE_FRAME = 1 << 3;
75     public static final int TYPE_WIDGETS_FULL_SHEET = 1 << 4;
76     public static final int TYPE_ON_BOARD_POPUP = 1 << 5;
77     public static final int TYPE_DISCOVERY_BOUNCE = 1 << 6;
78     public static final int TYPE_SNACKBAR = 1 << 7;
79     public static final int TYPE_LISTENER = 1 << 8;
80     public static final int TYPE_ALL_APPS_EDU = 1 << 9;
81     public static final int TYPE_DRAG_DROP_POPUP = 1 << 10;
82 
83     // Popups related to quickstep UI
84     public static final int TYPE_TASK_MENU = 1 << 11;
85     public static final int TYPE_OPTIONS_POPUP = 1 << 12;
86     public static final int TYPE_ICON_SURFACE = 1 << 13;
87 
88     public static final int TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP = 1 << 14;
89     public static final int TYPE_WIDGETS_EDUCATION_DIALOG = 1 << 15;
90 
91     public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
92             | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
93             | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
94             | TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER | TYPE_ALL_APPS_EDU
95             | TYPE_ICON_SURFACE | TYPE_DRAG_DROP_POPUP | TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP
96             | TYPE_WIDGETS_EDUCATION_DIALOG;
97 
98     // Type of popups which should be kept open during launcher rebind
99     public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
100             | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
101             | TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE | TYPE_WIDGETS_EDUCATION_DIALOG;
102 
103     // Usually we show the back button when a floating view is open. Instead, hide for these types.
104     public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
105             | TYPE_SNACKBAR | TYPE_WIDGET_RESIZE_FRAME | TYPE_LISTENER;
106 
107     public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER
108             & ~TYPE_ALL_APPS_EDU;
109 
110     // These view all have particular operation associated with swipe down interaction.
111     public static final int TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW = TYPE_WIDGETS_BOTTOM_SHEET |
112             TYPE_WIDGETS_FULL_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_ON_BOARD_POPUP |
113             TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU | TYPE_DRAG_DROP_POPUP;
114 
115     protected boolean mIsOpen;
116 
AbstractFloatingView(Context context, AttributeSet attrs)117     public AbstractFloatingView(Context context, AttributeSet attrs) {
118         super(context, attrs);
119     }
120 
AbstractFloatingView(Context context, AttributeSet attrs, int defStyleAttr)121     public AbstractFloatingView(Context context, AttributeSet attrs, int defStyleAttr) {
122         super(context, attrs, defStyleAttr);
123     }
124 
125     /**
126      * We need to handle touch events to prevent them from falling through to the workspace below.
127      */
128     @SuppressLint("ClickableViewAccessibility")
129     @Override
onTouchEvent(MotionEvent ev)130     public boolean onTouchEvent(MotionEvent ev) {
131         return true;
132     }
133 
close(boolean animate)134     public final void close(boolean animate) {
135         animate &= areAnimatorsEnabled();
136         if (mIsOpen) {
137             // Add to WW logging
138         }
139         handleClose(animate);
140         mIsOpen = false;
141     }
142 
handleClose(boolean animate)143     protected abstract void handleClose(boolean animate);
144 
145     /**
146      * Creates a user-controlled animation to hint that the view will be closed if completed.
147      * @param distanceToMove The max distance that elements should move from their starting point.
148      */
addHintCloseAnim( float distanceToMove, Interpolator interpolator, PendingAnimation target)149     public void addHintCloseAnim(
150             float distanceToMove, Interpolator interpolator, PendingAnimation target) { }
151 
isOpen()152     public final boolean isOpen() {
153         return mIsOpen;
154     }
155 
isOfType(@loatingViewType int type)156     protected abstract boolean isOfType(@FloatingViewType int type);
157 
158     /** @return Whether the back is consumed. If false, Launcher will handle the back as well. */
onBackPressed()159     public boolean onBackPressed() {
160         close(true);
161         return true;
162     }
163 
164     @Override
onControllerTouchEvent(MotionEvent ev)165     public boolean onControllerTouchEvent(MotionEvent ev) {
166         return false;
167     }
168 
announceAccessibilityChanges()169     protected void announceAccessibilityChanges() {
170         Pair<View, String> targetInfo = getAccessibilityTarget();
171         if (targetInfo == null || !isAccessibilityEnabled(getContext())) {
172             return;
173         }
174         sendCustomAccessibilityEvent(
175                 targetInfo.first, TYPE_WINDOW_STATE_CHANGED, targetInfo.second);
176 
177         if (mIsOpen) {
178             getAccessibilityInitialFocusView().performAccessibilityAction(
179                     AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
180         }
181         ActivityContext.lookupContext(getContext()).getDragLayer()
182                 .sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
183     }
184 
getAccessibilityTarget()185     protected Pair<View, String> getAccessibilityTarget() {
186         return null;
187     }
188 
189     /** Returns the View that Accessibility services should focus on first. */
getAccessibilityInitialFocusView()190     protected View getAccessibilityInitialFocusView() {
191         return this;
192     }
193 
194     /**
195      * Returns a view matching FloatingViewType
196      */
getOpenView( ActivityContext activity, @FloatingViewType int type)197     public static <T extends AbstractFloatingView> T getOpenView(
198             ActivityContext activity, @FloatingViewType int type) {
199         BaseDragLayer dragLayer = activity.getDragLayer();
200         if (dragLayer == null) return null;
201         // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
202         // and will be one of the last views.
203         for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
204             View child = dragLayer.getChildAt(i);
205             if (child instanceof AbstractFloatingView) {
206                 AbstractFloatingView view = (AbstractFloatingView) child;
207                 if (view.isOfType(type) && view.isOpen()) {
208                     return (T) view;
209                 }
210             }
211         }
212         return null;
213     }
214 
closeOpenContainer(ActivityContext activity, @FloatingViewType int type)215     public static void closeOpenContainer(ActivityContext activity,
216             @FloatingViewType int type) {
217         AbstractFloatingView view = getOpenView(activity, type);
218         if (view != null) {
219             view.close(true);
220         }
221     }
222 
closeOpenViews(ActivityContext activity, boolean animate, @FloatingViewType int type)223     public static void closeOpenViews(ActivityContext activity, boolean animate,
224             @FloatingViewType int type) {
225         BaseDragLayer dragLayer = activity.getDragLayer();
226         // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
227         // and will be one of the last views.
228         for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
229             View child = dragLayer.getChildAt(i);
230             if (child instanceof AbstractFloatingView) {
231                 AbstractFloatingView abs = (AbstractFloatingView) child;
232                 if (abs.isOfType(type)) {
233                     abs.close(animate);
234                 }
235             }
236         }
237     }
238 
closeAllOpenViews(ActivityContext activity, boolean animate)239     public static void closeAllOpenViews(ActivityContext activity, boolean animate) {
240         closeOpenViews(activity, animate, TYPE_ALL);
241         activity.finishAutoCancelActionMode();
242     }
243 
closeAllOpenViews(ActivityContext activity)244     public static void closeAllOpenViews(ActivityContext activity) {
245         closeAllOpenViews(activity, true);
246     }
247 
closeAllOpenViewsExcept(ActivityContext activity, boolean animate, @FloatingViewType int type)248     public static void closeAllOpenViewsExcept(ActivityContext activity, boolean animate,
249                                                @FloatingViewType int type) {
250         closeOpenViews(activity, animate, TYPE_ALL & ~type);
251         activity.finishAutoCancelActionMode();
252     }
253 
closeAllOpenViewsExcept(ActivityContext activity, @FloatingViewType int type)254     public static void closeAllOpenViewsExcept(ActivityContext activity,
255                                                @FloatingViewType int type) {
256         closeAllOpenViewsExcept(activity, true, type);
257     }
258 
getTopOpenView(ActivityContext activity)259     public static AbstractFloatingView getTopOpenView(ActivityContext activity) {
260         return getTopOpenViewWithType(activity, TYPE_ALL);
261     }
262 
getTopOpenViewWithType(ActivityContext activity, @FloatingViewType int type)263     public static AbstractFloatingView getTopOpenViewWithType(ActivityContext activity,
264             @FloatingViewType int type) {
265         return getOpenView(activity, type);
266     }
267 
canInterceptEventsInSystemGestureRegion()268     public boolean canInterceptEventsInSystemGestureRegion() {
269         return false;
270     }
271 }
272