blob: e8a2cb160880412dfb603ea655aa731eb15fbcd5 [file] [log] [blame]
/*
* 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.wm.shell.windowdecor;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.os.Handler;
import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.transition.Transitions;
/**
* View model for the window decoration with a caption and shadows. Works with
* {@link CaptionWindowDecoration}.
*/
public class CaptionWindowDecorViewModel implements WindowDecorViewModel<CaptionWindowDecoration> {
private final ActivityTaskManager mActivityTaskManager;
private final ShellTaskOrganizer mTaskOrganizer;
private final Context mContext;
private final Handler mMainHandler;
private final Choreographer mMainChoreographer;
private final DisplayController mDisplayController;
private final SyncTransactionQueue mSyncQueue;
private FreeformTaskTransitionStarter mTransitionStarter;
public CaptionWindowDecorViewModel(
Context context,
Handler mainHandler,
Choreographer mainChoreographer,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
SyncTransactionQueue syncQueue) {
mContext = context;
mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer;
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
mTaskOrganizer = taskOrganizer;
mDisplayController = displayController;
mSyncQueue = syncQueue;
}
@Override
public void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter) {
mTransitionStarter = transitionStarter;
}
@Override
public CaptionWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
if (!shouldShowWindowDecor(taskInfo)) return null;
final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
mContext,
mDisplayController,
mTaskOrganizer,
taskInfo,
taskSurface,
mMainHandler,
mMainChoreographer,
mSyncQueue);
TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration);
CaptionTouchEventListener touchEventListener =
new CaptionTouchEventListener(taskInfo, taskPositioner);
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
windowDecoration.setDragResizeCallback(taskPositioner);
setupWindowDecorationForTransition(taskInfo, startT, finishT, windowDecoration);
setupCaptionColor(taskInfo, windowDecoration);
return windowDecoration;
}
@Override
public CaptionWindowDecoration adoptWindowDecoration(AutoCloseable windowDecor) {
if (!(windowDecor instanceof CaptionWindowDecoration)) return null;
final CaptionWindowDecoration captionWindowDecor = (CaptionWindowDecoration) windowDecor;
if (!shouldShowWindowDecor(captionWindowDecor.mTaskInfo)) {
return null;
}
return captionWindowDecor;
}
@Override
public void onTaskInfoChanged(RunningTaskInfo taskInfo, CaptionWindowDecoration decoration) {
decoration.relayout(taskInfo);
setupCaptionColor(taskInfo, decoration);
}
private void setupCaptionColor(RunningTaskInfo taskInfo, CaptionWindowDecoration decoration) {
int statusBarColor = taskInfo.taskDescription.getStatusBarColor();
decoration.setCaptionColor(statusBarColor);
}
@Override
public void setupWindowDecorationForTransition(
RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT,
CaptionWindowDecoration decoration) {
decoration.relayout(taskInfo, startT, finishT);
}
private class CaptionTouchEventListener implements
View.OnClickListener, View.OnTouchListener {
private final int mTaskId;
private final WindowContainerToken mTaskToken;
private final DragResizeCallback mDragResizeCallback;
private int mDragPointerId = -1;
private CaptionTouchEventListener(
RunningTaskInfo taskInfo,
DragResizeCallback dragResizeCallback) {
mTaskId = taskInfo.taskId;
mTaskToken = taskInfo.token;
mDragResizeCallback = dragResizeCallback;
}
@Override
public void onClick(View v) {
final int id = v.getId();
if (id == R.id.close_window) {
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.removeTask(mTaskToken);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mTransitionStarter.startRemoveTransition(wct);
} else {
mSyncQueue.queue(wct);
}
} else if (id == R.id.maximize_window) {
WindowContainerTransaction wct = new WindowContainerTransaction();
RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
int targetWindowingMode = taskInfo.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
? WINDOWING_MODE_FULLSCREEN : WINDOWING_MODE_FREEFORM;
int displayWindowingMode =
taskInfo.configuration.windowConfiguration.getDisplayWindowingMode();
wct.setWindowingMode(mTaskToken,
targetWindowingMode == displayWindowingMode
? WINDOWING_MODE_UNDEFINED : targetWindowingMode);
if (targetWindowingMode == WINDOWING_MODE_FULLSCREEN) {
wct.setBounds(mTaskToken, null);
}
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mTransitionStarter.startWindowingModeTransition(targetWindowingMode, wct);
} else {
mSyncQueue.queue(wct);
}
} else if (id == R.id.minimize_window) {
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reorder(mTaskToken, false);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
mTransitionStarter.startMinimizedModeTransition(wct);
} else {
mSyncQueue.queue(wct);
}
}
}
@Override
public boolean onTouch(View v, MotionEvent e) {
if (v.getId() != R.id.caption) {
return false;
}
handleEventForMove(e);
if (e.getAction() != MotionEvent.ACTION_DOWN) {
return false;
}
RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
if (taskInfo.isFocused) {
return false;
}
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reorder(mTaskToken, true /* onTop */);
mSyncQueue.queue(wct);
return true;
}
private void handleEventForMove(MotionEvent e) {
if (mTaskOrganizer.getRunningTaskInfo(mTaskId).getWindowingMode()
== WINDOWING_MODE_FULLSCREEN) {
return;
}
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mDragPointerId = e.getPointerId(0);
mDragResizeCallback.onDragResizeStart(
0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
break;
case MotionEvent.ACTION_MOVE: {
int dragPointerIdx = e.findPointerIndex(mDragPointerId);
mDragResizeCallback.onDragResizeMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
int dragPointerIdx = e.findPointerIndex(mDragPointerId);
mDragResizeCallback.onDragResizeEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
break;
}
}
}
}
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
return DesktopMode.IS_SUPPORTED
&& mDisplayController.getDisplayContext(taskInfo.displayId)
.getResources().getConfiguration().smallestScreenWidthDp >= 600;
}
}