• 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.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
19 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
20 
21 import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
22 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
23 import static com.android.launcher3.LauncherState.ALL_APPS;
24 
25 import android.annotation.SuppressLint;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.graphics.PixelFormat;
29 import android.view.Gravity;
30 import android.view.MotionEvent;
31 import android.view.WindowManager;
32 import android.view.WindowManager.LayoutParams;
33 
34 import androidx.annotation.Nullable;
35 
36 import com.android.launcher3.AbstractFloatingView;
37 import com.android.launcher3.DeviceProfile;
38 import com.android.launcher3.taskbar.TaskbarActivityContext;
39 import com.android.launcher3.taskbar.TaskbarControllers;
40 import com.android.systemui.shared.system.TaskStackChangeListener;
41 import com.android.systemui.shared.system.TaskStackChangeListeners;
42 
43 import java.util.Optional;
44 
45 /**
46  * Handles the Taskbar overlay window lifecycle.
47  * <p>
48  * Overlays need to be inflated in a separate window so that have the correct hierarchy. For
49  * instance, they need to be below the notification tray. If there are multiple overlays open, the
50  * same window is used.
51  */
52 public final class TaskbarOverlayController {
53 
54     private static final String WINDOW_TITLE = "Taskbar Overlay";
55 
56     private final TaskbarActivityContext mTaskbarContext;
57     private final Context mWindowContext;
58     private final TaskbarOverlayProxyView mProxyView;
59     private final LayoutParams mLayoutParams;
60 
61     private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
62         @Override
63         public void onTaskCreated(int taskId, ComponentName componentName) {
64             // Created task will be below existing overlay, so move out of the way.
65             hideWindow();
66         }
67 
68         @Override
69         public void onTaskMovedToFront(int taskId) {
70             // New front task will be below existing overlay, so move out of the way.
71             hideWindow();
72         }
73     };
74 
75     private DeviceProfile mLauncherDeviceProfile;
76     private @Nullable TaskbarOverlayContext mOverlayContext;
77     private TaskbarControllers mControllers; // Initialized in init.
78 
TaskbarOverlayController( TaskbarActivityContext taskbarContext, DeviceProfile launcherDeviceProfile)79     public TaskbarOverlayController(
80             TaskbarActivityContext taskbarContext, DeviceProfile launcherDeviceProfile) {
81         mTaskbarContext = taskbarContext;
82         mWindowContext = mTaskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);
83         mProxyView = new TaskbarOverlayProxyView();
84         mLayoutParams = createLayoutParams();
85         mLauncherDeviceProfile = launcherDeviceProfile;
86     }
87 
88     /** Initialize the controller. */
init(TaskbarControllers controllers)89     public void init(TaskbarControllers controllers) {
90         mControllers = controllers;
91     }
92 
93     /**
94      * Creates a window for Taskbar overlays, if it does not already exist. Returns the window
95      * context for the current overlay window.
96      */
requestWindow()97     public TaskbarOverlayContext requestWindow() {
98         if (mOverlayContext == null) {
99             mOverlayContext = new TaskbarOverlayContext(
100                     mWindowContext, mTaskbarContext, mControllers);
101         }
102 
103         if (!mProxyView.isOpen()) {
104             mProxyView.show();
105             Optional.ofNullable(mOverlayContext.getSystemService(WindowManager.class))
106                     .ifPresent(m -> m.addView(mOverlayContext.getDragLayer(), mLayoutParams));
107             TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
108         }
109 
110         return mOverlayContext;
111     }
112 
113     /** Hides the current overlay window with animation. */
hideWindow()114     public void hideWindow() {
115         mProxyView.close(true);
116     }
117 
118     /**
119      * Removes the overlay window from the hierarchy, if all floating views are closed and there is
120      * no system drag operation in progress.
121      * <p>
122      * This method should be called after an exit animation finishes, if applicable.
123      */
124     @SuppressLint("WrongConstant")
maybeCloseWindow()125     void maybeCloseWindow() {
126         if (mOverlayContext != null && (AbstractFloatingView.hasOpenView(mOverlayContext, TYPE_ALL)
127                 || mOverlayContext.getDragController().isSystemDragInProgress())) {
128             return;
129         }
130         mProxyView.close(false);
131         onDestroy();
132     }
133 
134     /** Destroys the controller and any overlay window if present. */
onDestroy()135     public void onDestroy() {
136         TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
137         Optional.ofNullable(mOverlayContext)
138                 .map(c -> c.getSystemService(WindowManager.class))
139                 .ifPresent(m -> m.removeViewImmediate(mOverlayContext.getDragLayer()));
140         mOverlayContext = null;
141     }
142 
143     /** The current device profile for the overlay window. */
getLauncherDeviceProfile()144     public DeviceProfile getLauncherDeviceProfile() {
145         return mLauncherDeviceProfile;
146     }
147 
148     /** Updates {@link DeviceProfile} instance for Taskbar's overlay window. */
updateLauncherDeviceProfile(DeviceProfile dp)149     public void updateLauncherDeviceProfile(DeviceProfile dp) {
150         mLauncherDeviceProfile = dp;
151         Optional.ofNullable(mOverlayContext).ifPresent(c -> {
152             AbstractFloatingView.closeAllOpenViewsExcept(c, false, TYPE_REBIND_SAFE);
153             c.dispatchDeviceProfileChanged();
154         });
155     }
156 
157     /** The default open duration for overlays. */
getOpenDuration()158     public int getOpenDuration() {
159         return ALL_APPS.getTransitionDuration(mTaskbarContext, true);
160     }
161 
162     /** The default close duration for overlays. */
getCloseDuration()163     public int getCloseDuration() {
164         return ALL_APPS.getTransitionDuration(mTaskbarContext, false);
165     }
166 
167     @SuppressLint("WrongConstant")
createLayoutParams()168     private LayoutParams createLayoutParams() {
169         LayoutParams layoutParams = new LayoutParams(
170                 TYPE_APPLICATION_OVERLAY,
171                 LayoutParams.FLAG_SPLIT_TOUCH,
172                 PixelFormat.TRANSLUCENT);
173         layoutParams.setTitle(WINDOW_TITLE);
174         layoutParams.gravity = Gravity.BOTTOM;
175         layoutParams.packageName = mTaskbarContext.getPackageName();
176         layoutParams.setFitInsetsTypes(0); // Handled by container view.
177         layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
178         layoutParams.setSystemApplicationOverlay(true);
179         return layoutParams;
180     }
181 
182     /**
183      * Proxy view connecting taskbar drag layer to the overlay window.
184      *
185      * Overlays are in a separate window and has its own drag layer, but this proxy lets its views
186      * behave as though they are in the taskbar drag layer. For instance, when the taskbar closes
187      * all {@link AbstractFloatingView} instances, the overlay window will also close.
188      */
189     private class TaskbarOverlayProxyView extends AbstractFloatingView {
190 
TaskbarOverlayProxyView()191         private TaskbarOverlayProxyView() {
192             super(mTaskbarContext, null);
193         }
194 
show()195         private void show() {
196             mIsOpen = true;
197             mTaskbarContext.getDragLayer().addView(this);
198         }
199 
200         @Override
handleClose(boolean animate)201         protected void handleClose(boolean animate) {
202             mTaskbarContext.getDragLayer().removeView(this);
203             Optional.ofNullable(mOverlayContext).ifPresent(c -> closeAllOpenViews(c, animate));
204         }
205 
206         @Override
isOfType(int type)207         protected boolean isOfType(int type) {
208             return (type & TYPE_TASKBAR_OVERLAY_PROXY) != 0;
209         }
210 
211         @Override
onControllerInterceptTouchEvent(MotionEvent ev)212         public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
213             return false;
214         }
215     }
216 }
217