• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.taskbar.overlay;
17 
18 import static android.view.KeyEvent.ACTION_UP;
19 import static android.view.KeyEvent.KEYCODE_BACK;
20 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
21 
22 import android.content.Context;
23 import android.graphics.Insets;
24 import android.media.permission.SafeCloseable;
25 import android.view.KeyEvent;
26 import android.view.MotionEvent;
27 import android.view.View;
28 import android.view.ViewTreeObserver;
29 import android.view.WindowInsets;
30 
31 import androidx.annotation.NonNull;
32 
33 import com.android.app.viewcapture.ViewCaptureFactory;
34 import com.android.launcher3.AbstractFloatingView;
35 import com.android.launcher3.testing.TestLogging;
36 import com.android.launcher3.testing.shared.TestProtocol;
37 import com.android.launcher3.util.TouchController;
38 import com.android.launcher3.views.BaseDragLayer;
39 
40 import java.util.ArrayList;
41 import java.util.List;
42 
43 /** Root drag layer for the Taskbar overlay window. */
44 public class TaskbarOverlayDragLayer extends
45         BaseDragLayer<TaskbarOverlayContext> implements
46         ViewTreeObserver.OnComputeInternalInsetsListener {
47 
48     private SafeCloseable mViewCaptureCloseable;
49     private final List<TouchController> mTouchControllers = new ArrayList<>();
50 
TaskbarOverlayDragLayer(Context context)51     TaskbarOverlayDragLayer(Context context) {
52         super(context, null, 1);
53         setClipChildren(false);
54         recreateControllers();
55     }
56 
57     @Override
onAttachedToWindow()58     protected void onAttachedToWindow() {
59         super.onAttachedToWindow();
60         getViewTreeObserver().addOnComputeInternalInsetsListener(this);
61         mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())
62                 .startCapture(getRootView(), ".TaskbarOverlay");
63     }
64 
65     @Override
onDetachedFromWindow()66     protected void onDetachedFromWindow() {
67         super.onDetachedFromWindow();
68         getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
69         mViewCaptureCloseable.close();
70     }
71 
72     @Override
recreateControllers()73     public void recreateControllers() {
74         List<TouchController> controllers = new ArrayList<>();
75         controllers.add(mContainer.getDragController());
76         controllers.addAll(mTouchControllers);
77         mControllers = controllers.toArray(new TouchController[0]);
78     }
79 
80     @Override
dispatchTouchEvent(MotionEvent ev)81     public boolean dispatchTouchEvent(MotionEvent ev) {
82         TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
83         return super.dispatchTouchEvent(ev);
84     }
85 
86     @Override
dispatchKeyEvent(KeyEvent event)87     public boolean dispatchKeyEvent(KeyEvent event) {
88         if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
89             AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mContainer);
90             if (topView != null && topView.canHandleBack()) {
91                 topView.onBackInvoked();
92                 return true;
93             }
94         } else if (event.getAction() == KeyEvent.ACTION_DOWN
95                 && event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE && event.hasNoModifiers()) {
96             // Ignore escape if pressed in conjunction with any modifier keys. Close each
97             // floating view one at a time for each key press.
98             AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mContainer);
99             if (topView != null) {
100                 topView.close(/* animate= */ true);
101                 return true;
102             }
103         }
104         return super.dispatchKeyEvent(event);
105     }
106 
107     @Override
onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo)108     public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
109         if (mContainer.isAnySystemDragInProgress()) {
110             inoutInfo.touchableRegion.setEmpty();
111             inoutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
112         }
113     }
114 
115     @Override
onApplyWindowInsets(WindowInsets insets)116     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
117         insets = updateInsetsDueToStashing(insets);
118         setInsets(insets.getInsets(WindowInsets.Type.systemBars()).toRect());
119         return insets;
120     }
121 
122     @Override
onViewRemoved(View child)123     public void onViewRemoved(View child) {
124         super.onViewRemoved(child);
125         mContainer.getOverlayController().maybeCloseWindow();
126     }
127 
128     /** Adds a {@link TouchController} to this drag layer. */
addTouchController(@onNull TouchController touchController)129     public void addTouchController(@NonNull TouchController touchController) {
130         mTouchControllers.add(touchController);
131         recreateControllers();
132     }
133 
134     /** Removes a {@link TouchController} from this drag layer. */
removeTouchController(@onNull TouchController touchController)135     public void removeTouchController(@NonNull TouchController touchController) {
136         mTouchControllers.remove(touchController);
137         recreateControllers();
138     }
139 
140     /**
141      * Taskbar automatically stashes when opening all apps, but we don't report the insets as
142      * changing to avoid moving the underlying app. But internally, the apps view should still
143      * layout according to the stashed insets rather than the unstashed insets. So this method
144      * does two things:
145      * 1) Sets navigationBars bottom inset to stashedHeight.
146      * 2) Sets tappableInsets bottom inset to 0.
147      */
updateInsetsDueToStashing(WindowInsets oldInsets)148     private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
149         if (!mContainer.isTransientTaskbar()) {
150             return oldInsets;
151         }
152         WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
153 
154         Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
155         Insets newNavInsets = Insets.of(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
156                 mContainer.getStashedTaskbarHeight());
157         updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
158 
159         Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
160         Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
161                 oldTappableInsets.right, 0);
162         updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
163 
164         return updatedInsetsBuilder.build();
165     }
166 }
167