diff options
| author | 2024-08-20 13:34:45 -0700 | |
|---|---|---|
| committer | 2024-09-13 17:09:07 +0000 | |
| commit | ea16e0e34a10ff355e7832c598fa295a0461877c (patch) | |
| tree | 4a3db2c7a122fdd06b634b4b9d59fc50113aae6a | |
| parent | 2ca7c59b8de3ff9ab7a3ae084409e890878c0918 (diff) | |
Add ManageWindowsViewContainer.
Adds an abstract view container that will be used for Manage Windows
menu on both Shell and Taskbar sides. This class will create the base
menu view and populate it with icons, given a list of TaskSnapshots
mapped to their respective RecentTaskInfo.
The child classes will be responsible for providing this map as well as
defining onClick behavior for the instance icons.
Bug: 336289597
Bug: 337903443
Test: Manual
Flag: com.android.window.flags.enable_desktop_windowing_multi_instance_features
Change-Id: I600cfdf3d20834f9917dee87f56159a4155d46e5
| -rw-r--r-- | libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt new file mode 100644 index 000000000000..79becb0a2e20 --- /dev/null +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/ManageWindowsViewContainer.kt @@ -0,0 +1,173 @@ +/* + * 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.shared.desktopmode +import android.annotation.ColorInt +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.ShapeDrawable +import android.graphics.drawable.shapes.RoundRectShape +import android.util.TypedValue +import android.view.MotionEvent.ACTION_OUTSIDE +import android.view.SurfaceView +import android.view.ViewGroup.MarginLayoutParams +import android.widget.LinearLayout +import android.window.TaskSnapshot + +/** + * View for the All Windows menu option, used by both Desktop Windowing and Taskbar. + * The menu displays icons of all open instances of an app. Clicking the icon should launch + * the instance, which will be performed by the child class. + */ +abstract class ManageWindowsViewContainer( + val context: Context, + @ColorInt private val menuBackgroundColor: Int +) { + lateinit var menuView: ManageWindowsView + + /** Creates the base menu view and fills it with icon views. */ + fun show(snapshotList: List<Pair<Int, TaskSnapshot>>, + onIconClickListener: ((Int) -> Unit), + onOutsideClickListener: (() -> Unit)): ManageWindowsView { + menuView = ManageWindowsView(context, menuBackgroundColor).apply { + this.onOutsideClickListener = onOutsideClickListener + this.onIconClickListener = onIconClickListener + this.generateIconViews(snapshotList) + } + addToContainer(menuView) + return menuView + } + + /** Adds the menu view to the container responsible for displaying it. */ + abstract fun addToContainer(menuView: ManageWindowsView) + + /** Dispose of the menu, perform needed cleanup. */ + abstract fun close() + + companion object { + const val MANAGE_WINDOWS_MINIMUM_INSTANCES = 2 + } + + class ManageWindowsView( + private val context: Context, + menuBackgroundColor: Int + ) { + val rootView: LinearLayout = LinearLayout(context) + var menuHeight = 0 + var menuWidth = 0 + var onIconClickListener: ((Int) -> Unit)? = null + var onOutsideClickListener: (() -> Unit)? = null + + init { + rootView.orientation = LinearLayout.VERTICAL + val menuBackground = ShapeDrawable() + val menuRadius = getDimensionPixelSize(MENU_RADIUS_DP) + menuBackground.shape = RoundRectShape( + FloatArray(8) { menuRadius }, + null, + null + ) + menuBackground.paint.color = menuBackgroundColor + rootView.background = menuBackground + rootView.elevation = getDimensionPixelSize(MENU_ELEVATION_DP) + rootView.setOnTouchListener { _, event -> + if (event.actionMasked == ACTION_OUTSIDE) { + onOutsideClickListener?.invoke() + } + return@setOnTouchListener true + } + } + + private fun getDimensionPixelSize(sizeDp: Float): Float { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + sizeDp, context.resources.displayMetrics) + } + + fun generateIconViews( + snapshotList: List<Pair<Int, TaskSnapshot>> + ) { + menuWidth = 0 + menuHeight = 0 + rootView.removeAllViews() + val instanceIconHeight = getDimensionPixelSize(ICON_HEIGHT_DP) + val instanceIconWidth = getDimensionPixelSize(ICON_WIDTH_DP) + val iconRadius = getDimensionPixelSize(ICON_RADIUS_DP) + val iconMargin = getDimensionPixelSize(ICON_MARGIN_DP) + var rowLayout: LinearLayout? = null + // Add each icon to the menu, adding a new row when needed. + for ((iconCount, taskInfoSnapshotPair) in snapshotList.withIndex()) { + val taskId = taskInfoSnapshotPair.first + val snapshot = taskInfoSnapshotPair.second + // Once a row is filled, make a new row and increase the menu height. + if (iconCount % MENU_MAX_ICONS_PER_ROW == 0) { + rowLayout = LinearLayout(context) + rowLayout.orientation = LinearLayout.HORIZONTAL + rootView.addView(rowLayout) + menuHeight += (instanceIconHeight + iconMargin).toInt() + } + val snapshotBitmap = Bitmap.wrapHardwareBuffer( + snapshot.hardwareBuffer, + snapshot.colorSpace + ) + val scaledSnapshotBitmap = snapshotBitmap?.let { + Bitmap.createScaledBitmap( + it, instanceIconWidth.toInt(), instanceIconHeight.toInt(), true /* filter */ + ) + } + val appSnapshotButton = SurfaceView(context) + appSnapshotButton.cornerRadius = iconRadius + appSnapshotButton.setZOrderOnTop(true) + appSnapshotButton.setOnClickListener { + onIconClickListener?.invoke(taskId) + } + val lp = MarginLayoutParams( + instanceIconWidth.toInt(), instanceIconHeight.toInt() + ) + lp.apply { + marginStart = iconMargin.toInt() + topMargin = iconMargin.toInt() + } + appSnapshotButton.layoutParams = lp + // If we haven't already reached one full row, increment width. + if (iconCount < MENU_MAX_ICONS_PER_ROW) { + menuWidth += (instanceIconWidth + iconMargin).toInt() + } + rowLayout?.addView(appSnapshotButton) + appSnapshotButton.requestLayout() + rowLayout?.post { + appSnapshotButton.holder.surface + .attachAndQueueBufferWithColorSpace( + scaledSnapshotBitmap?.hardwareBuffer, + scaledSnapshotBitmap?.colorSpace + ) + } + } + // Add margin again for the right/bottom of the menu. + menuWidth += iconMargin.toInt() + menuHeight += iconMargin.toInt() + } + + companion object { + private const val MENU_RADIUS_DP = 26f + private const val ICON_WIDTH_DP = 204f + private const val ICON_HEIGHT_DP = 127.5f + private const val ICON_RADIUS_DP = 16f + private const val ICON_MARGIN_DP = 16f + private const val MENU_ELEVATION_DP = 1f + private const val MENU_MAX_ICONS_PER_ROW = 3 + } + } +} |