• 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.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
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.support.annotation.IntDef;
29 import android.util.AttributeSet;
30 import android.util.Pair;
31 import android.view.MotionEvent;
32 import android.view.View;
33 import android.widget.LinearLayout;
34 
35 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
36 import com.android.launcher3.util.TouchController;
37 import com.android.launcher3.views.BaseDragLayer;
38 
39 import java.lang.annotation.Retention;
40 import java.lang.annotation.RetentionPolicy;
41 
42 /**
43  * Base class for a View which shows a floating UI on top of the launcher UI.
44  */
45 public 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 
AbstractFloatingView(Context context, AttributeSet attrs)91     public AbstractFloatingView(Context context, AttributeSet attrs) {
92         super(context, attrs);
93     }
94 
AbstractFloatingView(Context context, AttributeSet attrs, int defStyleAttr)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
onTouchEvent(MotionEvent ev)104     public boolean onTouchEvent(MotionEvent ev) {
105         return true;
106     }
107 
close(boolean animate)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 
handleClose(boolean animate)118     protected abstract void handleClose(boolean animate);
119 
logActionCommand(int command)120     public abstract void logActionCommand(int command);
121 
isOpen()122     public final boolean isOpen() {
123         return mIsOpen;
124     }
125 
onWidgetsBound()126     protected void onWidgetsBound() {
127     }
128 
isOfType(@loatingViewType int type)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. */
onBackPressed()132     public boolean onBackPressed() {
133         logActionCommand(Action.Command.BACK);
134         close(true);
135         return true;
136     }
137 
138     @Override
onControllerTouchEvent(MotionEvent ev)139     public boolean onControllerTouchEvent(MotionEvent ev) {
140         return false;
141     }
142 
announceAccessibilityChanges()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 
getAccessibilityTarget()158     protected Pair<View, String> getAccessibilityTarget() {
159         return null;
160     }
161 
getOpenView( BaseDraggingActivity activity, @FloatingViewType int type)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 
closeOpenContainer(BaseDraggingActivity activity, @FloatingViewType int type)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 
closeOpenViews(BaseDraggingActivity activity, boolean animate, @FloatingViewType int type)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 
closeAllOpenViews(BaseDraggingActivity activity, boolean animate)203     public static void closeAllOpenViews(BaseDraggingActivity activity, boolean animate) {
204         closeOpenViews(activity, animate, TYPE_ALL);
205         activity.finishAutoCancelActionMode();
206     }
207 
closeAllOpenViews(BaseDraggingActivity activity)208     public static void closeAllOpenViews(BaseDraggingActivity activity) {
209         closeAllOpenViews(activity, true);
210     }
211 
getTopOpenView(BaseDraggingActivity activity)212     public static AbstractFloatingView getTopOpenView(BaseDraggingActivity activity) {
213         return getTopOpenViewWithType(activity, TYPE_ALL);
214     }
215 
getTopOpenViewWithType(BaseDraggingActivity activity, @FloatingViewType int type)216     public static AbstractFloatingView getTopOpenViewWithType(BaseDraggingActivity activity,
217             @FloatingViewType int type) {
218         return getOpenView(activity, type);
219     }
220 }
221