0aa30e2292
Originally gated onTaskMovedToFront behind prototype because looked like
it was causing overlay to close when it shouldn't. However, it turns out
it was actually onTaskStackChanged that was doing this.
Additionally, changing onTaskMovedToFront to close with animation, because this will fire if swiping up from all apps (going to overview so Launcher is considered as the task moving to front). Also, registered onTaskCreated to be a bit more thorough.
Test: Manual (EDU, All Apps, and EDU + All Apps)
Fix: 283373523
Flag: none
Change-Id: I4cd3969f91a93bab190b764a656d9cfc03d1ce09
(cherry picked from commit 34c6b871af)
217 lines
8.1 KiB
Java
217 lines
8.1 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.ComponentName;
|
|
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.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 onTaskCreated(int taskId, ComponentName componentName) {
|
|
// Created task will be below existing overlay, so move out of the way.
|
|
hideWindow();
|
|
}
|
|
|
|
@Override
|
|
public void onTaskMovedToFront(int taskId) {
|
|
// New front task will be below existing overlay, so move out of the way.
|
|
hideWindow();
|
|
}
|
|
};
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|