| /* |
| * 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 com.android.wm.shell.taskview; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.ActivityManager; |
| import android.app.ActivityOptions; |
| import android.app.PendingIntent; |
| 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.Rect; |
| import android.graphics.Region; |
| import android.view.SurfaceControl; |
| import android.view.SurfaceHolder; |
| import android.view.SurfaceView; |
| import android.view.View; |
| import android.view.ViewTreeObserver; |
| |
| import java.util.concurrent.Executor; |
| |
| /** |
| * A {@link SurfaceView} that can display a task. This is a concrete implementation for |
| * {@link TaskViewBase} which interacts {@link TaskViewTaskController}. |
| */ |
| public class TaskView extends SurfaceView implements SurfaceHolder.Callback, |
| ViewTreeObserver.OnComputeInternalInsetsListener, TaskViewBase { |
| /** Callback for listening task state. */ |
| public interface Listener { |
| /** |
| * Only called once when the surface has been created & the container is ready for |
| * launching activities. |
| */ |
| default void onInitialized() {} |
| |
| /** Called when the container can no longer launch activities. */ |
| default void onReleased() {} |
| |
| /** Called when a task is created inside the container. */ |
| default void onTaskCreated(int taskId, ComponentName name) {} |
| |
| /** Called when a task visibility changes. */ |
| default void onTaskVisibilityChanged(int taskId, boolean visible) {} |
| |
| /** Called when a task is about to be removed from the stack inside the container. */ |
| default void onTaskRemovalStarted(int taskId) {} |
| |
| /** Called when a task is created inside the container. */ |
| default void onBackPressedOnTaskRoot(int taskId) {} |
| } |
| |
| private final Rect mTmpRect = new Rect(); |
| private final Rect mTmpRootRect = new Rect(); |
| private final int[] mTmpLocation = new int[2]; |
| private final TaskViewTaskController mTaskViewTaskController; |
| private Region mObscuredTouchRegion; |
| |
| public TaskView(Context context, TaskViewTaskController taskViewTaskController) { |
| super(context, null, 0, 0, true /* disableBackgroundLayer */); |
| mTaskViewTaskController = taskViewTaskController; |
| // TODO(b/266736992): Think about a better way to set the TaskViewBase on the |
| // TaskViewTaskController and vice-versa |
| mTaskViewTaskController.setTaskViewBase(this); |
| getHolder().addCallback(this); |
| } |
| |
| /** |
| * Launch a new activity. |
| * |
| * @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. |
| * @param launchBounds the bounds (window size and position) that the activity should be |
| * launched in, in pixels and in screen coordinates. |
| */ |
| public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent, |
| @NonNull ActivityOptions options, @Nullable Rect launchBounds) { |
| mTaskViewTaskController.startActivity(pendingIntent, fillInIntent, options, launchBounds); |
| } |
| |
| /** |
| * Launch an activity represented by {@link ShortcutInfo}. |
| * <p>The owner of this container must be allowed to access the shortcut information, |
| * as defined in {@link LauncherApps#hasShortcutHostPermission()} to use this method. |
| * |
| * @param shortcut the shortcut used to launch the activity. |
| * @param options options for the activity. |
| * @param launchBounds the bounds (window size and position) that the activity should be |
| * launched in, in pixels and in screen coordinates. |
| */ |
| public void startShortcutActivity(@NonNull ShortcutInfo shortcut, |
| @NonNull ActivityOptions options, @Nullable Rect launchBounds) { |
| mTaskViewTaskController.startShortcutActivity(shortcut, options, launchBounds); |
| } |
| |
| @Override |
| public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { |
| onLocationChanged(); |
| if (taskInfo.taskDescription != null) { |
| setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); |
| } |
| } |
| |
| @Override |
| public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { |
| if (taskInfo.taskDescription != null) { |
| setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); |
| } |
| } |
| |
| /** |
| * @return {@code True} when the TaskView's surface has been created, {@code False} otherwise. |
| */ |
| public boolean isInitialized() { |
| return mTaskViewTaskController.isInitialized(); |
| } |
| |
| @Override |
| public Rect getCurrentBoundsOnScreen() { |
| getBoundsOnScreen(mTmpRect); |
| return mTmpRect; |
| } |
| |
| @Override |
| public void setResizeBgColor(SurfaceControl.Transaction t, int bgColor) { |
| setResizeBackgroundColor(t, bgColor); |
| } |
| |
| /** |
| * Only one listener may be set on the view, throws an exception otherwise. |
| */ |
| public void setListener(@NonNull Executor executor, TaskView.Listener listener) { |
| mTaskViewTaskController.setListener(executor, listener); |
| } |
| |
| /** |
| * Indicates a region of the view that is not touchable. |
| * |
| * @param obscuredRect the obscured region of the view. |
| */ |
| public void setObscuredTouchRect(Rect obscuredRect) { |
| mObscuredTouchRegion = obscuredRect != null ? new Region(obscuredRect) : null; |
| } |
| |
| /** |
| * Indicates a region of the view that is not touchable. |
| * |
| * @param obscuredRegion the obscured region of the view. |
| */ |
| public void setObscuredTouchRegion(Region obscuredRegion) { |
| mObscuredTouchRegion = obscuredRegion; |
| } |
| |
| /** |
| * Call when view position or size has changed. Do not call when animating. |
| */ |
| public void onLocationChanged() { |
| getBoundsOnScreen(mTmpRect); |
| mTaskViewTaskController.setWindowBounds(mTmpRect); |
| } |
| |
| /** |
| * Call to remove the task from window manager. This task will not appear in recents. |
| */ |
| public void removeTask() { |
| mTaskViewTaskController.removeTask(); |
| } |
| |
| /** |
| * Release this container if it is initialized. |
| */ |
| public void release() { |
| getHolder().removeCallback(this); |
| mTaskViewTaskController.release(); |
| } |
| |
| @Override |
| public String toString() { |
| return mTaskViewTaskController.toString(); |
| } |
| |
| @Override |
| public void surfaceCreated(SurfaceHolder holder) { |
| mTaskViewTaskController.surfaceCreated(getSurfaceControl()); |
| } |
| |
| @Override |
| public void surfaceChanged(@androidx.annotation.NonNull SurfaceHolder holder, int format, |
| int width, int height) { |
| getBoundsOnScreen(mTmpRect); |
| mTaskViewTaskController.setWindowBounds(mTmpRect); |
| } |
| |
| @Override |
| public void surfaceDestroyed(SurfaceHolder holder) { |
| mTaskViewTaskController.surfaceDestroyed(); |
| } |
| |
| @Override |
| public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) { |
| // TODO(b/176854108): Consider to move the logic into gatherTransparentRegions since this |
| // is dependent on the order of listener. |
| // If there are multiple TaskViews, we'll set the touchable area as the root-view, then |
| // subtract each TaskView from it. |
| if (inoutInfo.touchableRegion.isEmpty()) { |
| inoutInfo.setTouchableInsets( |
| ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); |
| View root = getRootView(); |
| root.getLocationInWindow(mTmpLocation); |
| mTmpRootRect.set(mTmpLocation[0], mTmpLocation[1], root.getWidth(), root.getHeight()); |
| inoutInfo.touchableRegion.set(mTmpRootRect); |
| } |
| getLocationInWindow(mTmpLocation); |
| mTmpRect.set(mTmpLocation[0], mTmpLocation[1], |
| mTmpLocation[0] + getWidth(), mTmpLocation[1] + getHeight()); |
| inoutInfo.touchableRegion.op(mTmpRect, Region.Op.DIFFERENCE); |
| |
| if (mObscuredTouchRegion != null) { |
| inoutInfo.touchableRegion.op(mObscuredTouchRegion, Region.Op.UNION); |
| } |
| } |
| |
| @Override |
| protected void onAttachedToWindow() { |
| super.onAttachedToWindow(); |
| getViewTreeObserver().addOnComputeInternalInsetsListener(this); |
| } |
| |
| @Override |
| protected void onDetachedFromWindow() { |
| super.onDetachedFromWindow(); |
| getViewTreeObserver().removeOnComputeInternalInsetsListener(this); |
| } |
| |
| /** Returns the task info for the task in the TaskView. */ |
| @Nullable |
| public ActivityManager.RunningTaskInfo getTaskInfo() { |
| return mTaskViewTaskController.getTaskInfo(); |
| } |
| } |