diff options
7 files changed, 222 insertions, 31 deletions
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt new file mode 100644 index 000000000000..4c76168cdeaa --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 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.bubbles + +import android.content.ComponentName +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.wm.shell.taskview.TaskView +import com.android.wm.shell.taskview.TaskViewTaskController + +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors.directExecutor +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.mock + +@SmallTest +@RunWith(AndroidJUnit4::class) +class BubbleTaskViewTest { + + private lateinit var bubbleTaskView: BubbleTaskView + private val context = ApplicationProvider.getApplicationContext<Context>() + + @Before + fun setUp() { + val taskView = TaskView(context, mock<TaskViewTaskController>()) + bubbleTaskView = BubbleTaskView(taskView, directExecutor()) + } + + @Test + fun onTaskCreated_updatesState() { + val componentName = ComponentName(context, "TestClass") + bubbleTaskView.listener.onTaskCreated(123, componentName) + + assertThat(bubbleTaskView.taskId).isEqualTo(123) + assertThat(bubbleTaskView.componentName).isEqualTo(componentName) + assertThat(bubbleTaskView.isCreated).isTrue() + } + + @Test + fun onTaskCreated_callsDelegateListener() { + var actualTaskId = -1 + var actualComponentName: ComponentName? = null + val delegateListener = object : TaskView.Listener { + override fun onTaskCreated(taskId: Int, name: ComponentName) { + actualTaskId = taskId + actualComponentName = name + } + } + bubbleTaskView.delegateListener = delegateListener + + val componentName = ComponentName(context, "TestClass") + bubbleTaskView.listener.onTaskCreated(123, componentName) + + assertThat(actualTaskId).isEqualTo(123) + assertThat(actualComponentName).isEqualTo(componentName) + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index f3fe895bf9b4..9f7d0ac9bafe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -74,7 +74,6 @@ import com.android.wm.shell.R; import com.android.wm.shell.common.AlphaOptimizedButton; import com.android.wm.shell.common.TriangleShape; import com.android.wm.shell.taskview.TaskView; -import com.android.wm.shell.taskview.TaskViewTaskController; import java.io.PrintWriter; @@ -146,7 +145,6 @@ public class BubbleExpandedView extends LinearLayout { private AlphaOptimizedButton mManageButton; private TaskView mTaskView; - private TaskViewTaskController mTaskViewTaskController; private BubbleOverflowContainerView mOverflowView; private int mTaskId = INVALID_TASK_ID; @@ -434,7 +432,8 @@ public class BubbleExpandedView extends LinearLayout { * Initialize {@link BubbleController} and {@link BubbleStackView} here, this method must need * to be called after view inflate. */ - void initialize(BubbleController controller, BubbleStackView stackView, boolean isOverflow) { + void initialize(BubbleController controller, BubbleStackView stackView, boolean isOverflow, + @Nullable BubbleTaskView bubbleTaskView) { mController = controller; mStackView = stackView; mIsOverflow = isOverflow; @@ -451,18 +450,22 @@ public class BubbleExpandedView extends LinearLayout { bringChildToFront(mOverflowView); mManageButton.setVisibility(GONE); } else { - mTaskViewTaskController = new TaskViewTaskController(mContext, - mController.getTaskOrganizer(), - mController.getTaskViewTransitions(), mController.getSyncTransactionQueue()); - mTaskView = new TaskView(mContext, mTaskViewTaskController); - mTaskView.setListener(mController.getMainExecutor(), mTaskViewListener); + mTaskView = bubbleTaskView.getTaskView(); + bubbleTaskView.setDelegateListener(mTaskViewListener); // set a fixed width so it is not recalculated as part of a rotation. the width will be // updated manually after the rotation. FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(getContentWidth(), MATCH_PARENT); + if (mTaskView.getParent() != null) { + ((ViewGroup) mTaskView.getParent()).removeView(mTaskView); + } mExpandedViewContainer.addView(mTaskView, lp); bringChildToFront(mTaskView); + if (bubbleTaskView.isCreated()) { + mTaskViewListener.onTaskCreated( + bubbleTaskView.getTaskId(), bubbleTaskView.getComponentName()); + } } } @@ -876,7 +879,7 @@ public class BubbleExpandedView extends LinearLayout { return; } boolean isNew = mBubble == null || didBackingContentChange(bubble); - if (isNew || bubble != null && bubble.getKey().equals(mBubble.getKey())) { + if (isNew || bubble.getKey().equals(mBubble.getKey())) { mBubble = bubble; mManageButton.setContentDescription(getResources().getString( R.string.bubbles_settings_button_description, bubble.getAppName())); @@ -1107,7 +1110,8 @@ public class BubbleExpandedView extends LinearLayout { * has been removed. * * If this view should be reused after this method is called, then - * {@link #initialize(BubbleController, BubbleStackView, boolean)} must be invoked first. + * {@link #initialize(BubbleController, BubbleStackView, boolean, BubbleTaskView)} + * must be invoked first. */ public void cleanUpExpandedState() { if (DEBUG_BUBBLE_EXPANDED_VIEW) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt index 22e836aacfc5..e5d9acedc903 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt @@ -29,6 +29,7 @@ import android.util.PathParser import android.view.LayoutInflater import android.view.View.VISIBLE import android.widget.FrameLayout +import androidx.core.content.ContextCompat import com.android.launcher3.icons.BubbleIconFactory import com.android.wm.shell.R import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView @@ -57,10 +58,16 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl /** Call before use and again if cleanUpExpandedState was called. */ fun initialize(controller: BubbleController, forBubbleBar: Boolean) { if (forBubbleBar) { - createBubbleBarExpandedView().initialize(controller, true /* isOverflow */) + createBubbleBarExpandedView() + .initialize(controller, /* isOverflow= */ true, /* bubbleTaskView= */ null) } else { createExpandedView() - .initialize(controller, controller.stackView, true /* isOverflow */) + .initialize( + controller, + controller.stackView, + /* isOverflow= */ true, + /* bubbleTaskView= */ null + ) } } @@ -113,7 +120,10 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl context, res.getDimensionPixelSize(R.dimen.bubble_size), res.getDimensionPixelSize(R.dimen.bubble_badge_size), - res.getColor(com.android.launcher3.icons.R.color.important_conversation), + ContextCompat.getColor( + context, + com.android.launcher3.icons.R.color.important_conversation + ), res.getDimensionPixelSize(com.android.internal.R.dimen.importance_ring_stroke_width) ) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt new file mode 100644 index 000000000000..2fcd133c7b20 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 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.bubbles + +import android.app.ActivityTaskManager.INVALID_TASK_ID +import android.content.ComponentName +import androidx.annotation.VisibleForTesting +import com.android.wm.shell.taskview.TaskView +import java.util.concurrent.Executor + +/** + * A wrapper class around [TaskView] for bubble expanded views. + * + * [delegateListener] allows callers to change listeners after a task has been created. + */ +class BubbleTaskView(val taskView: TaskView, executor: Executor) { + + /** Whether the task is already created. */ + var isCreated = false + private set + + /** The task id. */ + var taskId = INVALID_TASK_ID + private set + + /** The component name of the application running in the task. */ + var componentName: ComponentName? = null + private set + + /** [TaskView.Listener] for users of this class. */ + var delegateListener: TaskView.Listener? = null + + /** A [TaskView.Listener] that delegates to [delegateListener]. */ + @get:VisibleForTesting + val listener = object : TaskView.Listener { + override fun onInitialized() { + delegateListener?.onInitialized() + } + + override fun onReleased() { + delegateListener?.onReleased() + } + + override fun onTaskCreated(taskId: Int, name: ComponentName) { + delegateListener?.onTaskCreated(taskId, name) + this@BubbleTaskView.taskId = taskId + isCreated = true + componentName = name + } + + override fun onTaskVisibilityChanged(taskId: Int, visible: Boolean) { + delegateListener?.onTaskVisibilityChanged(taskId, visible) + } + + override fun onTaskRemovalStarted(taskId: Int) { + delegateListener?.onTaskRemovalStarted(taskId) + } + + override fun onBackPressedOnTaskRoot(taskId: Int) { + delegateListener?.onBackPressedOnTaskRoot(taskId) + } + } + + init { + taskView.setListener(executor, listener) + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java index f6c382fb5b3d..5855a81333d4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java @@ -35,10 +35,7 @@ import android.view.View; import androidx.annotation.Nullable; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.taskview.TaskView; -import com.android.wm.shell.taskview.TaskViewTaskController; /** * Handles creating and updating the {@link TaskView} associated with a {@link Bubble}. @@ -65,7 +62,6 @@ public class BubbleTaskViewHelper { private final Context mContext; private final BubbleController mController; - private final @ShellMainThread ShellExecutor mMainExecutor; private final BubbleTaskViewHelper.Listener mListener; private final View mParentView; @@ -73,7 +69,6 @@ public class BubbleTaskViewHelper { private Bubble mBubble; @Nullable private PendingIntent mPendingIntent; - private TaskViewTaskController mTaskViewTaskController; @Nullable private TaskView mTaskView; private int mTaskId = INVALID_TASK_ID; @@ -204,17 +199,18 @@ public class BubbleTaskViewHelper { public BubbleTaskViewHelper(Context context, BubbleController controller, BubbleTaskViewHelper.Listener listener, + BubbleTaskView bubbleTaskView, View parent) { mContext = context; mController = controller; - mMainExecutor = mController.getMainExecutor(); mListener = listener; mParentView = parent; - mTaskViewTaskController = new TaskViewTaskController(mContext, - mController.getTaskOrganizer(), - mController.getTaskViewTransitions(), mController.getSyncTransactionQueue()); - mTaskView = new TaskView(mContext, mTaskViewTaskController); - mTaskView.setListener(mMainExecutor, mTaskViewListener); + mTaskView = bubbleTaskView.getTaskView(); + bubbleTaskView.setDelegateListener(mTaskViewListener); + if (bubbleTaskView.isCreated()) { + mTaskId = bubbleTaskView.getTaskId(); + mListener.onTaskCreated(); + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java index bb30c5eeebcf..c3d899e7dac7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java @@ -46,6 +46,8 @@ import com.android.launcher3.icons.BubbleIconFactory; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; +import com.android.wm.shell.taskview.TaskView; +import com.android.wm.shell.taskview.TaskViewTaskController; import java.lang.ref.WeakReference; import java.util.Objects; @@ -173,10 +175,12 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask BubbleViewInfo info = new BubbleViewInfo(); if (!skipInflation && !b.isInflated()) { + BubbleTaskView bubbleTaskView = createBubbleTaskView(c, controller); LayoutInflater inflater = LayoutInflater.from(c); info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate( R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */); - info.bubbleBarExpandedView.initialize(controller, false /* isOverflow */); + info.bubbleBarExpandedView.initialize( + controller, false /* isOverflow */, bubbleTaskView); } if (!populateCommonInfo(info, c, b, iconFactory)) { @@ -201,9 +205,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask R.layout.bubble_view, stackView, false /* attachToRoot */); info.imageView.initialize(controller.getPositioner()); + BubbleTaskView bubbleTaskView = createBubbleTaskView(c, controller); info.expandedView = (BubbleExpandedView) inflater.inflate( R.layout.bubble_expanded_view, stackView, false /* attachToRoot */); - info.expandedView.initialize(controller, stackView, false /* isOverflow */); + info.expandedView.initialize( + controller, stackView, false /* isOverflow */, bubbleTaskView); } if (!populateCommonInfo(info, c, b, iconFactory)) { @@ -219,6 +225,15 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask } return info; } + + private static BubbleTaskView createBubbleTaskView( + Context context, BubbleController controller) { + TaskViewTaskController taskViewTaskController = new TaskViewTaskController(context, + controller.getTaskOrganizer(), + controller.getTaskViewTransitions(), controller.getSyncTransactionQueue()); + TaskView taskView = new TaskView(context, taskViewTaskController); + return new BubbleTaskView(taskView, controller.getMainExecutor()); + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 66c0c9640477..3cf23ac114ee 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -16,6 +16,8 @@ package com.android.wm.shell.bubbles.bar; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; @@ -27,6 +29,7 @@ import android.graphics.Rect; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.widget.FrameLayout; @@ -35,6 +38,7 @@ import com.android.wm.shell.R; import com.android.wm.shell.bubbles.Bubble; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.bubbles.BubbleOverflowContainerView; +import com.android.wm.shell.bubbles.BubbleTaskView; import com.android.wm.shell.bubbles.BubbleTaskViewHelper; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.taskview.TaskView; @@ -130,7 +134,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView } /** Set the BubbleController on the view, must be called before doing anything else. */ - public void initialize(BubbleController controller, boolean isOverflow) { + public void initialize(BubbleController controller, boolean isOverflow, + @Nullable BubbleTaskView bubbleTaskView) { mController = controller; mIsOverflow = isOverflow; @@ -140,14 +145,19 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView mOverflowView.setBubbleController(mController); addView(mOverflowView); } else { - + mTaskView = bubbleTaskView.getTaskView(); mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, mController, - /* listener= */ this, + /* listener= */ this, bubbleTaskView, /* viewParent= */ this); - mTaskView = mBubbleTaskViewHelper.getTaskView(); - addView(mTaskView); + if (mTaskView.getParent() != null) { + ((ViewGroup) mTaskView.getParent()).removeView(mTaskView); + } + FrameLayout.LayoutParams lp = + new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT); + addView(mTaskView, lp); mTaskView.setEnableSurfaceClipping(true); mTaskView.setCornerRadius(mCornerRadius); + mTaskView.setVisibility(VISIBLE); // Handle view needs to draw on top of task view. bringChildToFront(mHandleView); |