blob: 2fb46509f337d43d10dc42f72e4bf0f4ef8733b9 [file] [log] [blame]
/*
* Copyright (C) 2020 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 android.window;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.content.Context;
import android.graphics.Rect;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceControl;
/**
* A component which handles embedded display of tasks within another window. The embedded task can
* be presented using the SurfaceControl provided from {@link #getSurfaceControl()}.
*
* @hide
*/
public class TaskOrganizerTaskEmbedder extends TaskEmbedder {
private static final String TAG = "TaskOrgTaskEmbedder";
private static final boolean DEBUG = false;
private TaskOrganizer mTaskOrganizer;
private ActivityManager.RunningTaskInfo mTaskInfo;
private WindowContainerToken mTaskToken;
private SurfaceControl mTaskLeash;
private boolean mPendingNotifyBoundsChanged;
/**
* Constructs a new TaskEmbedder.
*
* @param context the context
* @param host the host for this embedded task
*/
public TaskOrganizerTaskEmbedder(Context context, TaskOrganizerTaskEmbedder.Host host) {
super(context, host);
}
/**
* Whether this container has been initialized.
*
* @return true if initialized
*/
@Override
public boolean isInitialized() {
return mTaskOrganizer != null;
}
@Override
public boolean onInitialize() {
if (DEBUG) {
log("onInitialize");
}
// Register the task organizer
mTaskOrganizer = new TaskOrganizerImpl();
// TODO(wm-shell): This currently prevents other organizers from controlling MULT_WINDOW
// windowing mode tasks. Plan is to migrate this to a wm-shell front-end when that
// infrastructure is ready.
mTaskOrganizer.registerOrganizer(WINDOWING_MODE_MULTI_WINDOW);
mTaskOrganizer.setInterceptBackPressedOnTaskRoot(true);
return true;
}
@Override
protected boolean onRelease() {
if (DEBUG) {
log("onRelease");
}
if (!isInitialized()) {
return false;
}
mTaskOrganizer.unregisterOrganizer();
resetTaskInfo();
return true;
}
/**
* Starts presentation of tasks in this container.
*/
@Override
public void start() {
if (DEBUG) {
log("start");
}
if (!isInitialized()) {
return;
}
if (mTaskToken == null) {
return;
}
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setHidden(mTaskToken, false /* hidden */);
WindowOrganizer.applyTransaction(wct);
// TODO(b/151449487): Only call callback once we enable synchronization
if (mListener != null) {
mListener.onTaskVisibilityChanged(getTaskId(), true);
}
}
/**
* Stops presentation of tasks in this container.
*/
@Override
public void stop() {
if (DEBUG) {
log("stop");
}
if (!isInitialized()) {
return;
}
if (mTaskToken == null) {
return;
}
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setHidden(mTaskToken, true /* hidden */);
WindowOrganizer.applyTransaction(wct);
// TODO(b/151449487): Only call callback once we enable synchronization
if (mListener != null) {
mListener.onTaskVisibilityChanged(getTaskId(), false);
}
}
/**
* This should be called whenever the position or size of the surface changes
* or if touchable areas above the surface are added or removed.
*/
@Override
public void notifyBoundsChanged() {
if (DEBUG) {
log("notifyBoundsChanged: screenBounds=" + mHost.getScreenBounds());
}
if (mTaskToken == null) {
mPendingNotifyBoundsChanged = true;
return;
}
mPendingNotifyBoundsChanged = false;
// Update based on the screen bounds
Rect screenBounds = mHost.getScreenBounds();
if (screenBounds.left < 0 || screenBounds.top < 0) {
screenBounds.offsetTo(0, 0);
}
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setBounds(mTaskToken, screenBounds);
// TODO(b/151449487): Enable synchronization
WindowOrganizer.applyTransaction(wct);
}
/**
* Injects a pair of down/up key events with keycode {@link KeyEvent#KEYCODE_BACK} to the
* virtual display.
*/
@Override
public void performBackPress() {
// Do nothing, the task org task should already have focus if the caller is not focused
return;
}
/** An opaque unique identifier for this task surface among others being managed by the app. */
@Override
public int getId() {
return getTaskId();
}
/**
* Check if container is ready to launch and create {@link ActivityOptions} to target the
* virtual display.
* @param options The existing options to amend, or null if the caller wants new options to be
* created
*/
@Override
protected ActivityOptions prepareActivityOptions(ActivityOptions options) {
options = super.prepareActivityOptions(options);
options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
return options;
}
private int getTaskId() {
return mTaskInfo != null
? mTaskInfo.taskId
: INVALID_TASK_ID;
}
private void resetTaskInfo() {
if (DEBUG) {
log("resetTaskInfo");
}
mTaskInfo = null;
mTaskToken = null;
mTaskLeash = null;
}
private void log(String msg) {
Log.d(TAG, "[" + System.identityHashCode(this) + "] " + msg);
}
private class TaskOrganizerImpl extends TaskOrganizer {
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
if (DEBUG) {
log("taskAppeared: " + taskInfo.taskId);
}
mTaskInfo = taskInfo;
mTaskToken = taskInfo.token;
mTaskLeash = leash;
mTransaction.reparent(mTaskLeash, mSurfaceControl)
.show(mTaskLeash)
.show(mSurfaceControl)
.apply();
if (mPendingNotifyBoundsChanged) {
// TODO: Either defer show or hide and synchronize show with the resize
notifyBoundsChanged();
}
mHost.post(() -> mHost.onTaskBackgroundColorChanged(TaskOrganizerTaskEmbedder.this,
taskInfo.taskDescription.getBackgroundColor()));
if (mListener != null) {
mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity);
}
}
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
mTaskInfo.taskDescription = taskInfo.taskDescription;
mHost.post(() -> mHost.onTaskBackgroundColorChanged(TaskOrganizerTaskEmbedder.this,
taskInfo.taskDescription.getBackgroundColor()));
}
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
if (DEBUG) {
log("taskVanished: " + taskInfo.taskId);
}
if (mTaskToken != null && (taskInfo == null
|| mTaskToken.asBinder().equals(taskInfo.token.asBinder()))) {
if (mListener != null) {
mListener.onTaskRemovalStarted(taskInfo.taskId);
}
resetTaskInfo();
}
}
@Override
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
if (mListener != null) {
mListener.onBackPressedOnTaskRoot(taskInfo.taskId);
}
}
}
}