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