Automatic sources dropoff on 2020-06-10 18:32:38.095721
The change is generated with prebuilt drop tool.
Change-Id: I24cbf6ba6db262a1ae1445db1427a08fee35b3b4
diff --git a/android/app/ActivityView.java b/android/app/ActivityView.java
new file mode 100644
index 0000000..635ed13
--- /dev/null
+++ b/android/app/ActivityView.java
@@ -0,0 +1,629 @@
+/**
+ * Copyright (c) 2017 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.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Insets;
+import android.graphics.Matrix;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.hardware.display.VirtualDisplay;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.IWindow;
+import android.view.IWindowManager;
+import android.view.KeyEvent;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.window.TaskEmbedder;
+import android.window.TaskOrganizerTaskEmbedder;
+import android.window.VirtualDisplayTaskEmbedder;
+
+import dalvik.system.CloseGuard;
+
+/**
+ * Task container that allows launching activities into itself.
+ * <p>Activity launching into this container is restricted by the same rules that apply to launching
+ * on VirtualDisplays.
+ * @hide
+ */
+@TestApi
+public class ActivityView extends ViewGroup implements android.window.TaskEmbedder.Host {
+
+ private static final String TAG = "ActivityView";
+
+ private android.window.TaskEmbedder mTaskEmbedder;
+
+ private final SurfaceView mSurfaceView;
+ private final SurfaceCallback mSurfaceCallback;
+
+ private final CloseGuard mGuard = CloseGuard.get();
+ private boolean mOpened; // Protected by mGuard.
+
+ private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
+
+ // For Host
+ private final Point mWindowPosition = new Point();
+ private final int[] mTmpArray = new int[2];
+ private final Rect mTmpRect = new Rect();
+ private final Matrix mScreenSurfaceMatrix = new Matrix();
+ private final Region mTapExcludeRegion = new Region();
+
+ public ActivityView(Context context) {
+ this(context, null /* attrs */);
+ }
+
+ public ActivityView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0 /* defStyle */);
+ }
+
+ public ActivityView(Context context, AttributeSet attrs, int defStyle) {
+ this(context, attrs, defStyle, false /*singleTaskInstance*/);
+ }
+
+ public ActivityView(Context context, AttributeSet attrs, int defStyle,
+ boolean singleTaskInstance) {
+ this(context, attrs, defStyle, singleTaskInstance, false /* usePublicVirtualDisplay */);
+ }
+
+ /**
+ * This constructor let's the caller explicitly request a public virtual display as the backing
+ * display. Using a public display is not recommended as it exposes it to other applications,
+ * but it might be needed for backwards compatibility.
+ */
+ public ActivityView(
+ @NonNull Context context, @NonNull AttributeSet attrs, int defStyle,
+ boolean singleTaskInstance, boolean usePublicVirtualDisplay) {
+ super(context, attrs, defStyle);
+ if (useTaskOrganizer()) {
+ mTaskEmbedder = new TaskOrganizerTaskEmbedder(context, this);
+ } else {
+ mTaskEmbedder = new VirtualDisplayTaskEmbedder(context, this, singleTaskInstance,
+ usePublicVirtualDisplay);
+ }
+ mSurfaceView = new SurfaceView(context);
+ // Since ActivityView#getAlpha has been overridden, we should use parent class's alpha
+ // as master to synchronize surface view's alpha value.
+ mSurfaceView.setAlpha(super.getAlpha());
+ mSurfaceView.setUseAlpha();
+ mSurfaceCallback = new SurfaceCallback();
+ mSurfaceView.getHolder().addCallback(mSurfaceCallback);
+ addView(mSurfaceView);
+
+ mOpened = true;
+ mGuard.open("release");
+ }
+
+ /** Callback that notifies when the container is ready or destroyed. */
+ public abstract static class StateCallback {
+
+ /**
+ * Called when the container is ready for launching activities. Calling
+ * {@link #startActivity(Intent)} prior to this callback will result in an
+ * {@link IllegalStateException}.
+ *
+ * @see #startActivity(Intent)
+ */
+ public abstract void onActivityViewReady(ActivityView view);
+
+ /**
+ * Called when the container can no longer launch activities. Calling
+ * {@link #startActivity(Intent)} after this callback will result in an
+ * {@link IllegalStateException}.
+ *
+ * @see #startActivity(Intent)
+ */
+ public abstract void onActivityViewDestroyed(ActivityView view);
+
+ /**
+ * Called when a task is created inside the container.
+ * This is a filtered version of {@link TaskStackListener}
+ */
+ public void onTaskCreated(int taskId, ComponentName componentName) { }
+
+ /**
+ * Called when a task visibility changes.
+ * @hide
+ */
+ public void onTaskVisibilityChanged(int taskId, boolean visible) { }
+
+ /**
+ * Called when a task is moved to the front of the stack inside the container.
+ * This is a filtered version of {@link TaskStackListener}
+ */
+ public void onTaskMovedToFront(int taskId) { }
+
+ /**
+ * Called when a task is about to be removed from the stack inside the container.
+ * This is a filtered version of {@link TaskStackListener}
+ */
+ public void onTaskRemovalStarted(int taskId) { }
+
+ /**
+ * Called when back is pressed on the root activity of the task.
+ * @hide
+ */
+ public void onBackPressedOnTaskRoot(int taskId) { }
+ }
+
+ /**
+ * Set the callback to be notified about state changes.
+ * <p>This class must finish initializing before {@link #startActivity(Intent)} can be called.
+ * <p>Note: If the instance was ready prior to this call being made, then
+ * {@link StateCallback#onActivityViewReady(ActivityView)} will be called from within
+ * this method call.
+ *
+ * @param callback The callback to report events to.
+ *
+ * @see StateCallback
+ * @see #startActivity(Intent)
+ */
+ public void setCallback(StateCallback callback) {
+ if (callback == null) {
+ mTaskEmbedder.setListener(null);
+ return;
+ }
+ mTaskEmbedder.setListener(new StateCallbackAdapter(callback));
+ }
+
+ /**
+ * Sets the corner radius for the Activity displayed here. The corners will be
+ * cropped from the window painted by the contained Activity.
+ *
+ * @param cornerRadius the radius for the corners, in pixels
+ * @hide
+ */
+ public void setCornerRadius(float cornerRadius) {
+ mSurfaceView.setCornerRadius(cornerRadius);
+ }
+
+ /**
+ * @hide
+ */
+ public float getCornerRadius() {
+ return mSurfaceView.getCornerRadius();
+ }
+
+ /**
+ * Control whether the surface is clipped to the same bounds as the View. If true, then
+ * the bounds set by {@link #setSurfaceClipBounds(Rect)} are applied to the surface as
+ * window-crop.
+ *
+ * @param clippingEnabled whether to enable surface clipping
+ * @hide
+ */
+ public void setSurfaceClippingEnabled(boolean clippingEnabled) {
+ mSurfaceView.setEnableSurfaceClipping(clippingEnabled);
+ }
+
+ /**
+ * Sets an area on the contained surface to which it will be clipped
+ * when it is drawn. Setting the value to null will remove the clip bounds
+ * and the surface will draw normally, using its full bounds.
+ *
+ * @param clipBounds The rectangular area, in the local coordinates of
+ * this view, to which future drawing operations will be clipped.
+ * @hide
+ */
+ public void setSurfaceClipBounds(Rect clipBounds) {
+ mSurfaceView.setClipBounds(clipBounds);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean getSurfaceClipBounds(Rect outRect) {
+ return mSurfaceView.getClipBounds(outRect);
+ }
+
+ /**
+ * Launch an activity represented by {@link ShortcutInfo} into this container.
+ * <p>The owner of this container must be allowed to access the shortcut information,
+ * as defined in {@link LauncherApps#hasShortcutHostPermission()} to use this method.
+ * <p>Activity resolved by the provided {@link ShortcutInfo} must have
+ * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
+ * launched here. Also, if activity is not owned by the owner of this container, it must allow
+ * embedding and the caller must have permission to embed.
+ * <p>Note: This class must finish initializing and
+ * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
+ * this method can be called.
+ *
+ * @param shortcut the shortcut used to launch the activity.
+ * @param options for the activity.
+ * @param sourceBounds the rect containing the source bounds of the clicked icon to open
+ * this shortcut.
+ * @see StateCallback
+ * @see LauncherApps#startShortcut(ShortcutInfo, Rect, Bundle)
+ *
+ * @hide
+ */
+ public void startShortcutActivity(@NonNull ShortcutInfo shortcut,
+ @NonNull ActivityOptions options, @Nullable Rect sourceBounds) {
+ mTaskEmbedder.startShortcutActivity(shortcut, options, sourceBounds);
+ }
+
+ /**
+ * Launch a new activity into this container.
+ * <p>Activity resolved by the provided {@link Intent} must have
+ * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
+ * launched here. Also, if activity is not owned by the owner of this container, it must allow
+ * embedding and the caller must have permission to embed.
+ * <p>Note: This class must finish initializing and
+ * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
+ * this method can be called.
+ *
+ * @param intent Intent used to launch an activity.
+ *
+ * @see StateCallback
+ * @see #startActivity(PendingIntent)
+ */
+ public void startActivity(@NonNull Intent intent) {
+ mTaskEmbedder.startActivity(intent);
+ }
+
+ /**
+ * Launch a new activity into this container.
+ * <p>Activity resolved by the provided {@link Intent} must have
+ * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
+ * launched here. Also, if activity is not owned by the owner of this container, it must allow
+ * embedding and the caller must have permission to embed.
+ * <p>Note: This class must finish initializing and
+ * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
+ * this method can be called.
+ *
+ * @param intent Intent used to launch an activity.
+ * @param user The UserHandle of the user to start this activity for.
+ *
+ *
+ * @see StateCallback
+ * @see #startActivity(PendingIntent)
+ */
+ public void startActivity(@NonNull Intent intent, UserHandle user) {
+ mTaskEmbedder.startActivity(intent, user);
+ }
+
+ /**
+ * Launch a new activity into this container.
+ * <p>Activity resolved by the provided {@link PendingIntent} must have
+ * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
+ * launched here. Also, if activity is not owned by the owner of this container, it must allow
+ * embedding and the caller must have permission to embed.
+ * <p>Note: This class must finish initializing and
+ * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
+ * this method can be called.
+ *
+ * @param pendingIntent Intent used to launch an activity.
+ *
+ * @see StateCallback
+ * @see #startActivity(Intent)
+ */
+ public void startActivity(@NonNull PendingIntent pendingIntent) {
+ mTaskEmbedder.startActivity(pendingIntent);
+ }
+
+ /**
+ * Launch a new activity into this container.
+ * <p>Activity resolved by the provided {@link PendingIntent} must have
+ * {@link android.R.attr#resizeableActivity} attribute set to {@code true} in order to be
+ * launched here. Also, if activity is not owned by the owner of this container, it must allow
+ * embedding and the caller must have permission to embed.
+ * <p>Note: This class must finish initializing and
+ * {@link StateCallback#onActivityViewReady(ActivityView)} callback must be triggered before
+ * this method can be called.
+ *
+ * @param pendingIntent Intent used to launch an activity.
+ * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}.
+ * @param options options for the activity
+ *
+ * @see StateCallback
+ * @see #startActivity(Intent)
+ */
+ public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
+ @NonNull ActivityOptions options) {
+ mTaskEmbedder.startActivity(pendingIntent, fillInIntent, options);
+ }
+
+ /**
+ * Release this container. Activity launching will no longer be permitted.
+ * <p>Note: Calling this method is allowed after
+ * {@link StateCallback#onActivityViewReady(ActivityView)} callback was triggered and before
+ * {@link StateCallback#onActivityViewDestroyed(ActivityView)}.
+ *
+ * @see StateCallback
+ */
+ public void release() {
+ if (!mTaskEmbedder.isInitialized()) {
+ throw new IllegalStateException(
+ "Trying to release container that is not initialized.");
+ }
+ performRelease();
+ }
+
+ /**
+ * Triggers an update of {@link ActivityView}'s location in window to properly set tap exclude
+ * regions and avoid focus switches by touches on this view.
+ */
+ public void onLocationChanged() {
+ mTaskEmbedder.notifyBoundsChanged();
+ }
+
+ @Override
+ public void onLayout(boolean changed, int l, int t, int r, int b) {
+ mSurfaceView.layout(0 /* left */, 0 /* top */, r - l /* right */, b - t /* bottom */);
+ }
+
+ /**
+ * Sets the alpha value when the content of {@link SurfaceView} needs to show or hide.
+ * <p>Note: The surface view may ignore the alpha value in some cases. Refer to
+ * {@link SurfaceView#setAlpha} for more details.
+ *
+ * @param alpha The opacity of the view.
+ */
+ @Override
+ public void setAlpha(float alpha) {
+ super.setAlpha(alpha);
+
+ if (mSurfaceView != null) {
+ mSurfaceView.setAlpha(alpha);
+ }
+ }
+
+ @Override
+ public float getAlpha() {
+ return mSurfaceView.getAlpha();
+ }
+
+ @Override
+ public boolean gatherTransparentRegion(Region region) {
+ return mTaskEmbedder.gatherTransparentRegion(region)
+ || super.gatherTransparentRegion(region);
+ }
+
+ private class SurfaceCallback implements SurfaceHolder.Callback {
+ @Override
+ public void surfaceCreated(SurfaceHolder surfaceHolder) {
+ if (!mTaskEmbedder.isInitialized()) {
+ initTaskEmbedder(mSurfaceView.getSurfaceControl());
+ } else {
+ mTmpTransaction.reparent(mTaskEmbedder.getSurfaceControl(),
+ mSurfaceView.getSurfaceControl()).apply();
+ }
+ mTaskEmbedder.start();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
+ mTaskEmbedder.resizeTask(width, height);
+ mTaskEmbedder.notifyBoundsChanged();
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
+ mTaskEmbedder.stop();
+ }
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ mSurfaceView.setVisibility(visibility);
+ }
+
+ /**
+ * @return the display id of the virtual display.
+ */
+ public int getVirtualDisplayId() {
+ return mTaskEmbedder.getDisplayId();
+ }
+
+ /**
+ * @hide
+ * @return virtual display.
+ */
+ public VirtualDisplay getVirtualDisplay() {
+ return mTaskEmbedder.getVirtualDisplay();
+ }
+
+ /**
+ * Injects a pair of down/up key events with keycode {@link KeyEvent#KEYCODE_BACK} to the
+ * virtual display.
+ */
+ public void performBackPress() {
+ mTaskEmbedder.performBackPress();
+ }
+
+ /**
+ * Initializes the task embedder.
+ *
+ * @param parent control for the surface to parent to
+ * @return true if the task embedder has been initialized
+ */
+ private boolean initTaskEmbedder(SurfaceControl parent) {
+ if (!mTaskEmbedder.initialize(parent)) {
+ Log.e(TAG, "Failed to initialize ActivityView");
+ return false;
+ }
+ return true;
+ }
+
+ private void performRelease() {
+ if (!mOpened) {
+ return;
+ }
+ mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
+ mTaskEmbedder.release();
+ mTaskEmbedder.setListener(null);
+
+ mGuard.close();
+ mOpened = false;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mGuard != null) {
+ mGuard.warnIfOpen();
+ performRelease();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Set forwarded insets on the virtual display.
+ *
+ * @see IWindowManager#setForwardedInsets
+ */
+ public void setForwardedInsets(Insets insets) {
+ mTaskEmbedder.setForwardedInsets(insets);
+ }
+
+ // Host
+
+ /** @hide */
+ @Override
+ public void onTaskBackgroundColorChanged(android.window.TaskEmbedder ts, int bgColor) {
+ if (mSurfaceView != null) {
+ mSurfaceView.setResizeBackgroundColor(bgColor);
+ }
+ }
+
+ /** @hide */
+ @Override
+ public Region getTapExcludeRegion() {
+ if (isAttachedToWindow() && canReceivePointerEvents()) {
+ Point windowPos = getPositionInWindow();
+ mTapExcludeRegion.set(
+ windowPos.x,
+ windowPos.y,
+ windowPos.x + getWidth(),
+ windowPos.y + getHeight());
+ // There might be views on top of us. We need to subtract those areas from the tap
+ // exclude region.
+ final ViewParent parent = getParent();
+ if (parent != null) {
+ parent.subtractObscuredTouchableRegion(mTapExcludeRegion, this);
+ }
+ } else {
+ mTapExcludeRegion.setEmpty();
+ }
+ return mTapExcludeRegion;
+ }
+
+ /** @hide */
+ @Override
+ public Matrix getScreenToTaskMatrix() {
+ getLocationOnScreen(mTmpArray);
+ mScreenSurfaceMatrix.set(getMatrix());
+ mScreenSurfaceMatrix.postTranslate(mTmpArray[0], mTmpArray[1]);
+ return mScreenSurfaceMatrix;
+ }
+
+ /** @hide */
+ @Override
+ public Point getPositionInWindow() {
+ getLocationInWindow(mTmpArray);
+ mWindowPosition.set(mTmpArray[0], mTmpArray[1]);
+ return mWindowPosition;
+ }
+
+ /** @hide */
+ @Override
+ public Rect getScreenBounds() {
+ getBoundsOnScreen(mTmpRect);
+ return mTmpRect;
+ }
+
+ /** @hide */
+ @Override
+ public IWindow getWindow() {
+ return super.getWindow();
+ }
+
+ /** @hide */
+ @Override
+ public boolean canReceivePointerEvents() {
+ return super.canReceivePointerEvents();
+ }
+
+ /**
+ * Overridden by instances that require the use of the task organizer implementation instead of
+ * the virtual display implementation. Not for general use.
+ * @hide
+ */
+ protected boolean useTaskOrganizer() {
+ return false;
+ }
+
+ private final class StateCallbackAdapter implements TaskEmbedder.Listener {
+ private final StateCallback mCallback;
+
+ private StateCallbackAdapter(ActivityView.StateCallback cb) {
+ mCallback = cb;
+ }
+
+ @Override
+ public void onInitialized() {
+ mCallback.onActivityViewReady(ActivityView.this);
+ }
+
+ @Override
+ public void onReleased() {
+ mCallback.onActivityViewDestroyed(ActivityView.this);
+ }
+
+ @Override
+ public void onTaskCreated(int taskId, ComponentName name) {
+ mCallback.onTaskCreated(taskId, name);
+ }
+
+ @Override
+ public void onTaskVisibilityChanged(int taskId, boolean visible) {
+ mCallback.onTaskVisibilityChanged(taskId, visible);
+ }
+
+ @Override
+ public void onTaskMovedToFront(int taskId) {
+ mCallback.onTaskMovedToFront(taskId);
+ }
+
+ @Override
+ public void onTaskRemovalStarted(int taskId) {
+ mCallback.onTaskRemovalStarted(taskId);
+ }
+
+ @Override
+ public void onBackPressedOnTaskRoot(int taskId) {
+ mCallback.onBackPressedOnTaskRoot(taskId);
+ }
+ }
+}