368 lines
14 KiB
Java
368 lines
14 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2021 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.view.WindowManager.TRANSIT_CHANGE;
|
||
|
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||
|
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
|
||
|
import static android.view.WindowManager.TRANSIT_NONE;
|
||
|
import static android.view.WindowManager.TRANSIT_OPEN;
|
||
|
|
||
|
import android.annotation.CallSuper;
|
||
|
import android.annotation.FlaggedApi;
|
||
|
import android.annotation.IntDef;
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.Nullable;
|
||
|
import android.annotation.RequiresPermission;
|
||
|
import android.annotation.TestApi;
|
||
|
import android.os.Bundle;
|
||
|
import android.os.IBinder;
|
||
|
import android.os.RemoteException;
|
||
|
import android.view.RemoteAnimationDefinition;
|
||
|
import android.view.WindowManager;
|
||
|
|
||
|
import com.android.window.flags.Flags;
|
||
|
|
||
|
import java.lang.annotation.Retention;
|
||
|
import java.lang.annotation.RetentionPolicy;
|
||
|
import java.util.concurrent.Executor;
|
||
|
|
||
|
/**
|
||
|
* Interface for WindowManager to delegate control of {@code TaskFragment}.
|
||
|
* @hide
|
||
|
*/
|
||
|
@TestApi
|
||
|
public class TaskFragmentOrganizer extends WindowOrganizer {
|
||
|
|
||
|
/**
|
||
|
* Key to the {@link Throwable} in {@link TaskFragmentTransaction.Change#getErrorBundle()}.
|
||
|
*/
|
||
|
public static final String KEY_ERROR_CALLBACK_THROWABLE = "fragment_throwable";
|
||
|
|
||
|
/**
|
||
|
* Key to the {@link TaskFragmentInfo} in
|
||
|
* {@link TaskFragmentTransaction.Change#getErrorBundle()}.
|
||
|
*/
|
||
|
public static final String KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO = "task_fragment_info";
|
||
|
|
||
|
/**
|
||
|
* Key to the {@link TaskFragmentOperation.OperationType} in
|
||
|
* {@link TaskFragmentTransaction.Change#getErrorBundle()}.
|
||
|
*/
|
||
|
public static final String KEY_ERROR_CALLBACK_OP_TYPE = "operation_type";
|
||
|
|
||
|
/**
|
||
|
* No change set.
|
||
|
*/
|
||
|
@WindowManager.TransitionType
|
||
|
@TaskFragmentTransitionType
|
||
|
public static final int TASK_FRAGMENT_TRANSIT_NONE = TRANSIT_NONE;
|
||
|
|
||
|
/**
|
||
|
* A window that didn't exist before has been created and made visible.
|
||
|
*/
|
||
|
@WindowManager.TransitionType
|
||
|
@TaskFragmentTransitionType
|
||
|
public static final int TASK_FRAGMENT_TRANSIT_OPEN = TRANSIT_OPEN;
|
||
|
|
||
|
/**
|
||
|
* A window that was visible no-longer exists (was finished or destroyed).
|
||
|
*/
|
||
|
@WindowManager.TransitionType
|
||
|
@TaskFragmentTransitionType
|
||
|
public static final int TASK_FRAGMENT_TRANSIT_CLOSE = TRANSIT_CLOSE;
|
||
|
|
||
|
/**
|
||
|
* A window is visible before and after but changes in some way (eg. it resizes or changes
|
||
|
* windowing-mode).
|
||
|
*/
|
||
|
@WindowManager.TransitionType
|
||
|
@TaskFragmentTransitionType
|
||
|
public static final int TASK_FRAGMENT_TRANSIT_CHANGE = TRANSIT_CHANGE;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* The task fragment drag resize transition used by activity embedding.
|
||
|
*
|
||
|
* This value is also used in Transitions.TRANSIT_TASK_FRAGMENT_DRAG_RESIZE and must not
|
||
|
* conflict with other predefined transition types.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@WindowManager.TransitionType
|
||
|
@TaskFragmentTransitionType
|
||
|
public static final int TASK_FRAGMENT_TRANSIT_DRAG_RESIZE = TRANSIT_FIRST_CUSTOM + 17;
|
||
|
|
||
|
/**
|
||
|
* Introduced a sub set of {@link WindowManager.TransitionType} for the types that are used for
|
||
|
* TaskFragment transition.
|
||
|
*
|
||
|
* Doing this instead of exposing {@link WindowManager.TransitionType} because we want to keep
|
||
|
* the Shell transition API hidden until it comes fully stable.
|
||
|
* @hide
|
||
|
*/
|
||
|
@IntDef(prefix = { "TASK_FRAGMENT_TRANSIT_" }, value = {
|
||
|
TASK_FRAGMENT_TRANSIT_NONE,
|
||
|
TASK_FRAGMENT_TRANSIT_OPEN,
|
||
|
TASK_FRAGMENT_TRANSIT_CLOSE,
|
||
|
TASK_FRAGMENT_TRANSIT_CHANGE,
|
||
|
TASK_FRAGMENT_TRANSIT_DRAG_RESIZE,
|
||
|
})
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
public @interface TaskFragmentTransitionType {}
|
||
|
|
||
|
/**
|
||
|
* Creates a {@link Bundle} with an exception, operation type and TaskFragmentInfo (if any)
|
||
|
* that can be passed to {@link ITaskFragmentOrganizer#onTaskFragmentError}.
|
||
|
* @hide
|
||
|
*/
|
||
|
public static @NonNull Bundle putErrorInfoInBundle(@NonNull Throwable exception,
|
||
|
@Nullable TaskFragmentInfo info, @TaskFragmentOperation.OperationType int opType) {
|
||
|
final Bundle errorBundle = new Bundle();
|
||
|
errorBundle.putSerializable(KEY_ERROR_CALLBACK_THROWABLE, exception);
|
||
|
if (info != null) {
|
||
|
errorBundle.putParcelable(KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, info);
|
||
|
}
|
||
|
errorBundle.putInt(KEY_ERROR_CALLBACK_OP_TYPE, opType);
|
||
|
return errorBundle;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Callbacks from WM Core are posted on this executor.
|
||
|
*/
|
||
|
private final Executor mExecutor;
|
||
|
|
||
|
public TaskFragmentOrganizer(@NonNull Executor executor) {
|
||
|
mExecutor = executor;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the executor to run callbacks on.
|
||
|
*/
|
||
|
@NonNull
|
||
|
public Executor getExecutor() {
|
||
|
return mExecutor;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Registers a {@link TaskFragmentOrganizer} to manage TaskFragments.
|
||
|
*/
|
||
|
@CallSuper
|
||
|
public void registerOrganizer() {
|
||
|
// TODO(b/302420256) point to registerOrganizer(boolean) when flag is removed.
|
||
|
try {
|
||
|
getController().registerOrganizer(mInterface, false /* isSystemOrganizer */);
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Registers a {@link TaskFragmentOrganizer} to manage TaskFragments.
|
||
|
*
|
||
|
* Registering a system organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer
|
||
|
* will have additional system capabilities, including: (1) it will receive SurfaceControl for
|
||
|
* the organized TaskFragment, and (2) it needs to update the
|
||
|
* {@link android.view.SurfaceControl} following the window change accordingly.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@CallSuper
|
||
|
@RequiresPermission(value = "android.permission.MANAGE_ACTIVITY_TASKS", conditional = true)
|
||
|
@FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG)
|
||
|
public void registerOrganizer(boolean isSystemOrganizer) {
|
||
|
try {
|
||
|
getController().registerOrganizer(mInterface, isSystemOrganizer);
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Unregisters a previously registered TaskFragmentOrganizer.
|
||
|
*/
|
||
|
@CallSuper
|
||
|
public void unregisterOrganizer() {
|
||
|
try {
|
||
|
getController().unregisterOrganizer(mInterface);
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Registers remote animations per transition type for the organizer. It will override the
|
||
|
* animations if the transition only contains windows that belong to the organized
|
||
|
* TaskFragments, and at least one of the transition window is embedded (not filling the Task).
|
||
|
* @hide
|
||
|
*/
|
||
|
@CallSuper
|
||
|
public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) {
|
||
|
try {
|
||
|
getController().registerRemoteAnimations(mInterface, definition);
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Unregisters remote animations per transition type for the organizer.
|
||
|
* @hide
|
||
|
*/
|
||
|
@CallSuper
|
||
|
public void unregisterRemoteAnimations() {
|
||
|
try {
|
||
|
getController().unregisterRemoteAnimations(mInterface);
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Notifies the server that the organizer has finished handling the given transaction. The
|
||
|
* server should apply the given {@link WindowContainerTransaction} for the necessary changes.
|
||
|
*
|
||
|
* @param transactionToken {@link TaskFragmentTransaction#getTransactionToken()} from
|
||
|
* {@link #onTransactionReady(TaskFragmentTransaction)}
|
||
|
* @param wct {@link WindowContainerTransaction} that the server should apply for
|
||
|
* update of the transaction.
|
||
|
* @param transitionType {@link TaskFragmentTransitionType} if it needs to start a
|
||
|
* transition.
|
||
|
* @param shouldApplyIndependently If {@code true}, the {@code wct} will request a new
|
||
|
* transition, which will be queued until the sync engine is
|
||
|
* free if there is any other active sync. If {@code false},
|
||
|
* the {@code wct} will be directly applied to the active sync.
|
||
|
* @see com.android.server.wm.WindowOrganizerController#enforceTaskFragmentOrganizerPermission
|
||
|
* for permission enforcement.
|
||
|
*/
|
||
|
public void onTransactionHandled(@NonNull IBinder transactionToken,
|
||
|
@NonNull WindowContainerTransaction wct,
|
||
|
@TaskFragmentTransitionType int transitionType, boolean shouldApplyIndependently) {
|
||
|
wct.setTaskFragmentOrganizer(mInterface);
|
||
|
try {
|
||
|
getController().onTransactionHandled(transactionToken, wct, transitionType,
|
||
|
shouldApplyIndependently);
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Must use {@link #applyTransaction(WindowContainerTransaction, int, boolean)} instead.
|
||
|
* @see #applyTransaction(WindowContainerTransaction, int, boolean)
|
||
|
*/
|
||
|
@Override
|
||
|
public void applyTransaction(@NonNull WindowContainerTransaction wct) {
|
||
|
throw new RuntimeException("Not allowed!");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Requests the server to apply the given {@link WindowContainerTransaction}.
|
||
|
*
|
||
|
* @param wct {@link WindowContainerTransaction} to apply.
|
||
|
* @param transitionType {@link TaskFragmentTransitionType} if it needs to start a
|
||
|
* transition.
|
||
|
* @param shouldApplyIndependently If {@code true}, the {@code wct} will request a new
|
||
|
* transition, which will be queued until the sync engine is
|
||
|
* free if there is any other active sync. If {@code false},
|
||
|
* the {@code wct} will be directly applied to the active sync.
|
||
|
* @see com.android.server.wm.WindowOrganizerController#enforceTaskFragmentOrganizerPermission
|
||
|
* for permission enforcement.
|
||
|
*/
|
||
|
public void applyTransaction(@NonNull WindowContainerTransaction wct,
|
||
|
@TaskFragmentTransitionType int transitionType, boolean shouldApplyIndependently) {
|
||
|
if (wct.isEmpty()) {
|
||
|
return;
|
||
|
}
|
||
|
wct.setTaskFragmentOrganizer(mInterface);
|
||
|
try {
|
||
|
getController().applyTransaction(
|
||
|
wct, transitionType, shouldApplyIndependently, null /* remoteTransition */);
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Applies a transaction with a {@link RemoteTransition}. Only a system organizer is allowed to
|
||
|
* use {@link RemoteTransition}. See {@link TaskFragmentOrganizer#registerOrganizer(boolean)}.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG)
|
||
|
public void applySystemTransaction(@NonNull WindowContainerTransaction wct,
|
||
|
@TaskFragmentTransitionType int transitionType,
|
||
|
@Nullable RemoteTransition remoteTransition) {
|
||
|
if (wct.isEmpty()) {
|
||
|
return;
|
||
|
}
|
||
|
wct.setTaskFragmentOrganizer(mInterface);
|
||
|
try {
|
||
|
getController().applyTransaction(
|
||
|
wct, transitionType, remoteTransition != null /* shouldApplyIndependently */,
|
||
|
remoteTransition);
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Called when the transaction is ready so that the organizer can update the TaskFragments based
|
||
|
* on the changes in transaction.
|
||
|
*/
|
||
|
public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
|
||
|
// Notify the server to finish the transaction.
|
||
|
onTransactionHandled(transaction.getTransactionToken(), new WindowContainerTransaction(),
|
||
|
TASK_FRAGMENT_TRANSIT_NONE, false /* shouldApplyIndependently */);
|
||
|
}
|
||
|
|
||
|
private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
|
||
|
@Override
|
||
|
public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
|
||
|
mExecutor.execute(() -> TaskFragmentOrganizer.this.onTransactionReady(transaction));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
private final TaskFragmentOrganizerToken mToken = new TaskFragmentOrganizerToken(mInterface);
|
||
|
|
||
|
@NonNull
|
||
|
public TaskFragmentOrganizerToken getOrganizerToken() {
|
||
|
return mToken;
|
||
|
}
|
||
|
|
||
|
private ITaskFragmentOrganizerController getController() {
|
||
|
try {
|
||
|
return getWindowOrganizerController().getTaskFragmentOrganizerController();
|
||
|
} catch (RemoteException e) {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
|
||
|
* only occupies a portion of Task bounds.
|
||
|
* @hide
|
||
|
*/
|
||
|
public boolean isActivityEmbedded(@NonNull IBinder activityToken) {
|
||
|
try {
|
||
|
return getController().isActivityEmbedded(activityToken);
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
}
|