Files
Lawnchair/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
T
Brian Isganitis 1fbae12a98 Only close overlays onTaskMovedToFront for desktop prototype.
Test: Overlays stay open when task moves to front when prototype
disabled.
Fix: 280483451

Change-Id: Iba345d30e6339e76fbdf19a33fde78e4441d6065
2023-05-02 20:36:18 +00:00

217 lines
8.0 KiB
Java

/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.taskbar.overlay;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.LauncherState.ALL_APPS;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.quickstep.views.DesktopTaskView;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import java.util.Optional;
/**
* Handles the Taskbar overlay window lifecycle.
* <p>
* Overlays need to be inflated in a separate window so that have the correct hierarchy. For
* instance, they need to be below the notification tray. If there are multiple overlays open, the
* same window is used.
*/
public final class TaskbarOverlayController {
private static final String WINDOW_TITLE = "Taskbar Overlay";
private final TaskbarActivityContext mTaskbarContext;
private final Context mWindowContext;
private final TaskbarOverlayProxyView mProxyView;
private final LayoutParams mLayoutParams;
private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskStackChanged() {
mProxyView.close(false);
}
@Override
public void onTaskMovedToFront(int taskId) {
if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
mProxyView.close(false);
}
}
};
private DeviceProfile mLauncherDeviceProfile;
private @Nullable TaskbarOverlayContext mOverlayContext;
private TaskbarControllers mControllers; // Initialized in init.
public TaskbarOverlayController(
TaskbarActivityContext taskbarContext, DeviceProfile launcherDeviceProfile) {
mTaskbarContext = taskbarContext;
mWindowContext = mTaskbarContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);
mProxyView = new TaskbarOverlayProxyView();
mLayoutParams = createLayoutParams();
mLauncherDeviceProfile = launcherDeviceProfile;
}
/** Initialize the controller. */
public void init(TaskbarControllers controllers) {
mControllers = controllers;
}
/**
* Creates a window for Taskbar overlays, if it does not already exist. Returns the window
* context for the current overlay window.
*/
public TaskbarOverlayContext requestWindow() {
if (mOverlayContext == null) {
mOverlayContext = new TaskbarOverlayContext(
mWindowContext, mTaskbarContext, mControllers);
}
if (!mProxyView.isOpen()) {
mProxyView.show();
Optional.ofNullable(mOverlayContext.getSystemService(WindowManager.class))
.ifPresent(m -> m.addView(mOverlayContext.getDragLayer(), mLayoutParams));
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
}
return mOverlayContext;
}
/** Hides the current overlay window with animation. */
public void hideWindow() {
mProxyView.close(true);
}
/**
* Removes the overlay window from the hierarchy, if all floating views are closed and there is
* no system drag operation in progress.
* <p>
* This method should be called after an exit animation finishes, if applicable.
*/
@SuppressLint("WrongConstant")
void maybeCloseWindow() {
if (mOverlayContext != null && (AbstractFloatingView.hasOpenView(mOverlayContext, TYPE_ALL)
|| mOverlayContext.getDragController().isSystemDragInProgress())) {
return;
}
mProxyView.close(false);
onDestroy();
}
/** Destroys the controller and any overlay window if present. */
public void onDestroy() {
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
Optional.ofNullable(mOverlayContext)
.map(c -> c.getSystemService(WindowManager.class))
.ifPresent(m -> m.removeViewImmediate(mOverlayContext.getDragLayer()));
mOverlayContext = null;
}
/** The current device profile for the overlay window. */
public DeviceProfile getLauncherDeviceProfile() {
return mLauncherDeviceProfile;
}
/** Updates {@link DeviceProfile} instance for Taskbar's overlay window. */
public void updateLauncherDeviceProfile(DeviceProfile dp) {
mLauncherDeviceProfile = dp;
Optional.ofNullable(mOverlayContext).ifPresent(c -> {
AbstractFloatingView.closeAllOpenViewsExcept(c, false, TYPE_REBIND_SAFE);
c.dispatchDeviceProfileChanged();
});
}
/** The default open duration for overlays. */
public int getOpenDuration() {
return ALL_APPS.getTransitionDuration(mTaskbarContext, true);
}
/** The default close duration for overlays. */
public int getCloseDuration() {
return ALL_APPS.getTransitionDuration(mTaskbarContext, false);
}
@SuppressLint("WrongConstant")
private LayoutParams createLayoutParams() {
LayoutParams layoutParams = new LayoutParams(
TYPE_APPLICATION_OVERLAY,
LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
layoutParams.setTitle(WINDOW_TITLE);
layoutParams.gravity = Gravity.BOTTOM;
layoutParams.packageName = mTaskbarContext.getPackageName();
layoutParams.setFitInsetsTypes(0); // Handled by container view.
layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
layoutParams.setSystemApplicationOverlay(true);
return layoutParams;
}
/**
* Proxy view connecting taskbar drag layer to the overlay window.
*
* Overlays are in a separate window and has its own drag layer, but this proxy lets its views
* behave as though they are in the taskbar drag layer. For instance, when the taskbar closes
* all {@link AbstractFloatingView} instances, the overlay window will also close.
*/
private class TaskbarOverlayProxyView extends AbstractFloatingView {
private TaskbarOverlayProxyView() {
super(mTaskbarContext, null);
}
private void show() {
mIsOpen = true;
mTaskbarContext.getDragLayer().addView(this);
}
@Override
protected void handleClose(boolean animate) {
mTaskbarContext.getDragLayer().removeView(this);
Optional.ofNullable(mOverlayContext).ifPresent(c -> closeAllOpenViews(c, animate));
}
@Override
protected boolean isOfType(int type) {
return (type & TYPE_TASKBAR_OVERLAY_PROXY) != 0;
}
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
return false;
}
}
}