1fbae12a98
Test: Overlays stay open when task moves to front when prototype disabled. Fix: 280483451 Change-Id: Iba345d30e6339e76fbdf19a33fde78e4441d6065
217 lines
8.0 KiB
Java
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;
|
|
}
|
|
}
|
|
}
|