Merge "Initial screenshot tests for bubble bar view" into main

This commit is contained in:
Liran Binyamin
2024-09-12 13:33:50 +00:00
committed by Android (Google) Code Review
4 changed files with 238 additions and 41 deletions
+8
View File
@@ -64,3 +64,11 @@ filegroup {
"tests/multivalentScreenshotTests/src/**/*.kt",
],
}
filegroup {
name: "launcher3-quickstep-testing",
path: "testing",
srcs: [
"testing/**/*.kt",
],
}
@@ -0,0 +1,78 @@
/*
* 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.launcher3.taskbar.bubbles.testing
import android.app.Notification
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.PathParser
import android.view.LayoutInflater
import android.view.ViewGroup
import com.android.launcher3.R
import com.android.launcher3.taskbar.bubbles.BubbleBarBubble
import com.android.launcher3.taskbar.bubbles.BubbleView
import com.android.wm.shell.shared.bubbles.BubbleInfo
object FakeBubbleViewFactory {
/** Inflates a [BubbleView] and adds it to the [parent] view if it is present. */
fun createBubble(
context: Context,
key: String,
parent: ViewGroup?,
iconSize: Int = 50,
iconColor: Int,
badgeColor: Int = Color.RED,
dotColor: Int = Color.BLUE,
suppressNotification: Boolean = false,
): BubbleView {
val inflater = LayoutInflater.from(context)
// BubbleView uses launcher's badge to icon ratio and expects the badge image to already
// have the right size
val badgeToIconRatio = 0.444f
val badgeRadius = iconSize * badgeToIconRatio / 2
val icon = createCircleBitmap(radius = iconSize / 2, color = iconColor)
val badge = createCircleBitmap(radius = badgeRadius.toInt(), color = badgeColor)
val flags =
if (suppressNotification) Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION else 0
val bubbleInfo =
BubbleInfo(key, flags, null, null, 0, context.packageName, null, null, false, true)
val bubbleView = inflater.inflate(R.layout.bubblebar_item_view, parent, false) as BubbleView
val dotPath =
PathParser.createPathFromPathData(
context.resources.getString(com.android.internal.R.string.config_icon_mask)
)
val bubble =
BubbleBarBubble(bubbleInfo, bubbleView, badge, icon, dotColor, dotPath, "test app")
bubbleView.setBubble(bubble)
return bubbleView
}
private fun createCircleBitmap(radius: Int, color: Int): Bitmap {
val bitmap = Bitmap.createBitmap(radius * 2, radius * 2, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.drawARGB(0, 0, 0, 0)
val paint = Paint()
paint.color = color
canvas.drawCircle(radius.toFloat(), radius.toFloat(), radius.toFloat(), paint)
return bitmap
}
}
@@ -0,0 +1,141 @@
/*
* 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.launcher3.taskbar.bubbles
import android.content.Context
import android.graphics.Color
import android.platform.test.rule.ScreenRecordRule
import android.view.View
import android.widget.FrameLayout
import android.widget.FrameLayout.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout.LayoutParams.WRAP_CONTENT
import androidx.activity.ComponentActivity
import androidx.test.core.app.ApplicationProvider
import com.android.launcher3.R
import com.android.launcher3.taskbar.bubbles.testing.FakeBubbleViewFactory
import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
import platform.test.screenshot.DeviceEmulationSpec
import platform.test.screenshot.Displays
import platform.test.screenshot.ViewScreenshotTestRule
import platform.test.screenshot.getEmulatedDevicePathConfig
/** Screenshot tests for [BubbleBarView]. */
@RunWith(ParameterizedAndroidJunit4::class)
@ScreenRecordRule.ScreenRecord
class BubbleBarViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var bubbleBarView: BubbleBarView
companion object {
@Parameters(name = "{0}")
@JvmStatic
fun getTestSpecs() =
DeviceEmulationSpec.forDisplays(
Displays.Phone,
isDarkTheme = false,
isLandscape = false,
)
}
@get:Rule
val screenshotRule =
ViewScreenshotTestRule(
emulationSpec,
ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec)),
)
@Test
fun bubbleBarView_collapsed_oneBubble() {
screenshotRule.screenshotTest("bubbleBarView_collapsed_oneBubble") { activity ->
activity.actionBar?.hide()
setupBubbleBarView()
bubbleBarView.addBubble(createBubble("key1", Color.GREEN))
val container = FrameLayout(context)
val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
container.layoutParams = lp
container.addView(bubbleBarView)
container
}
}
@Test
fun bubbleBarView_collapsed_twoBubbles() {
screenshotRule.screenshotTest("bubbleBarView_collapsed_twoBubbles") { activity ->
activity.actionBar?.hide()
setupBubbleBarView()
bubbleBarView.addBubble(createBubble("key1", Color.GREEN))
bubbleBarView.addBubble(createBubble("key2", Color.CYAN))
val container = FrameLayout(context)
val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
container.layoutParams = lp
container.addView(bubbleBarView)
container
}
}
@Test
fun bubbleBarView_expanded_threeBubbles() {
// if we're still expanding, wait with taking a screenshot
val shouldWait: (ComponentActivity, View) -> Boolean = { _, _ -> bubbleBarView.isExpanding }
screenshotRule.screenshotTest(
"bubbleBarView_expanded_threeBubbles",
checkView = shouldWait,
) { activity ->
activity.actionBar?.hide()
setupBubbleBarView()
bubbleBarView.addBubble(createBubble("key1", Color.GREEN))
bubbleBarView.addBubble(createBubble("key2", Color.CYAN))
bubbleBarView.addBubble(createBubble("key3", Color.MAGENTA))
val container = FrameLayout(context)
val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
container.layoutParams = lp
container.addView(bubbleBarView)
bubbleBarView.isExpanded = true
container
}
}
private fun setupBubbleBarView() {
bubbleBarView = BubbleBarView(context)
val lp = FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)
bubbleBarView.layoutParams = lp
val paddingTop =
context.resources.getDimensionPixelSize(R.dimen.bubblebar_pointer_visible_size)
bubbleBarView.setPadding(0, paddingTop, 0, 0)
bubbleBarView.visibility = View.VISIBLE
bubbleBarView.alpha = 1f
}
private fun createBubble(key: String, color: Int): BubbleView {
val bubbleView =
FakeBubbleViewFactory.createBubble(
context,
key,
parent = bubbleBarView,
iconColor = color,
)
bubbleView.showDotIfNeeded(1f)
bubbleBarView.setSelectedBubble(bubbleView)
return bubbleView
}
}
@@ -15,17 +15,10 @@
*/
package com.android.launcher3.taskbar.bubbles
import android.app.Notification
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.PathParser
import android.view.LayoutInflater
import androidx.test.core.app.ApplicationProvider
import com.android.launcher3.R
import com.android.wm.shell.shared.bubbles.BubbleInfo
import com.android.launcher3.taskbar.bubbles.testing.FakeBubbleViewFactory
import com.google.android.apps.nexuslauncher.imagecomparison.goldenpathmanager.ViewScreenshotGoldenPathManager
import org.junit.Rule
import org.junit.Test
@@ -50,7 +43,7 @@ class BubbleViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
DeviceEmulationSpec.forDisplays(
Displays.Phone,
isDarkTheme = false,
isLandscape = false
isLandscape = false,
)
}
@@ -58,7 +51,7 @@ class BubbleViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
val screenshotRule =
ViewScreenshotTestRule(
emulationSpec,
ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec))
ViewScreenshotGoldenPathManager(getEmulatedDevicePathConfig(emulationSpec)),
)
@Test
@@ -86,39 +79,16 @@ class BubbleViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
}
private fun setupBubbleView(suppressNotification: Boolean = false): BubbleView {
val inflater = LayoutInflater.from(context)
val iconSize = 100
// BubbleView uses launcher's badge to icon ratio and expects the badge image to already
// have the right size
val badgeToIconRatio = 0.444f
val badgeRadius = iconSize * badgeToIconRatio / 2
val icon = createCircleBitmap(radius = iconSize / 2, color = Color.LTGRAY)
val badge = createCircleBitmap(radius = badgeRadius.toInt(), color = Color.RED)
val flags =
if (suppressNotification) Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION else 0
val bubbleInfo =
BubbleInfo("key", flags, null, null, 0, context.packageName, null, null, false, true)
val bubbleView = inflater.inflate(R.layout.bubblebar_item_view, null) as BubbleView
val dotPath =
PathParser.createPathFromPathData(
context.resources.getString(com.android.internal.R.string.config_icon_mask)
val bubbleView =
FakeBubbleViewFactory.createBubble(
context,
key = "key",
parent = null,
iconSize = 100,
iconColor = Color.LTGRAY,
suppressNotification = suppressNotification,
)
val bubble =
BubbleBarBubble(bubbleInfo, bubbleView, badge, icon, Color.BLUE, dotPath, "test app")
bubbleView.setBubble(bubble)
bubbleView.showDotIfNeeded(1f)
return bubbleView
}
private fun createCircleBitmap(radius: Int, color: Int): Bitmap {
val bitmap = Bitmap.createBitmap(radius * 2, radius * 2, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.drawARGB(0, 0, 0, 0)
val paint = Paint()
paint.color = color
canvas.drawCircle(radius.toFloat(), radius.toFloat(), radius.toFloat(), paint)
return bitmap
}
}