Widget Picker: Update UI layer to support shortcuts
Updates the relevant references to read from the appropriate widget info object. Bug: 370950552 Flag: com.android.launcher3.enable_widget_picker_refactor Test: Open picker for home screen and lockscreen hosts Change-Id: I1b2a543b69686d859775618a406901591a32f555
This commit is contained in:
1 parent
bb86bde284
commit
d885e21660
7 files changed
+275
-273
No files matched your search
+50
-37
@@ -16,15 +16,16 @@
|
||||
|
||||
package com.android.launcher3.widgetpicker.ui.components
|
||||
|
||||
import android.appwidget.AppWidgetProviderInfo
|
||||
import android.content.ClipData
|
||||
import android.content.ClipDescription
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import android.os.UserHandle
|
||||
import android.view.View
|
||||
import android.view.View.DragShadowBuilder
|
||||
import androidx.compose.ui.unit.Dp
|
||||
@@ -32,33 +33,31 @@ import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawable
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetInfo
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Information about the image's dimensions post scaling.
|
||||
*/
|
||||
/** Information about the image's dimensions post scaling. */
|
||||
data class ImageScaledDimensions(
|
||||
val scale: Float,
|
||||
val scaledSizeDp: DpSize,
|
||||
val scaledSizePx: IntSize,
|
||||
val scaledRadiusDp: Dp,
|
||||
val scaledRadiusPx: Float
|
||||
val scaledRadiusPx: Float,
|
||||
)
|
||||
|
||||
/**
|
||||
* A [DragShadowBuilder] that draws drag shadow using the provided bitmap and image dimensions.
|
||||
*/
|
||||
/** A [DragShadowBuilder] that draws drag shadow using the provided bitmap and image dimensions. */
|
||||
class ImageBitmapDragShadowBuilder(
|
||||
context: Context,
|
||||
bitmap: Bitmap,
|
||||
imageScaledDimensions: ImageScaledDimensions
|
||||
imageScaledDimensions: ImageScaledDimensions,
|
||||
) : DragShadowBuilder() {
|
||||
private val shadowWidth = imageScaledDimensions.scaledSizePx.width
|
||||
private val shadowHeight = imageScaledDimensions.scaledSizePx.height
|
||||
|
||||
private val shadowDrawable: RoundedBitmapDrawable =
|
||||
RoundedBitmapDrawableFactory.create(context.resources, bitmap)
|
||||
.apply { cornerRadius = imageScaledDimensions.scaledRadiusPx }
|
||||
RoundedBitmapDrawableFactory.create(context.resources, bitmap).apply {
|
||||
cornerRadius = imageScaledDimensions.scaledRadiusPx
|
||||
}
|
||||
|
||||
override fun onProvideShadowMetrics(outShadowSize: Point?, outShadowTouchPoint: Point?) {
|
||||
outShadowSize?.set(shadowWidth, shadowHeight)
|
||||
@@ -84,48 +83,62 @@ object TransparentDragShadowBuilder : DragShadowBuilder() {
|
||||
private const val SHADOW_SIZE = 10
|
||||
|
||||
override fun onDrawShadow(canvas: Canvas) {}
|
||||
|
||||
override fun onProvideShadowMetrics(outShadowSize: Point, outShadowTouchPoint: Point) {
|
||||
outShadowSize.set(SHADOW_SIZE, SHADOW_SIZE);
|
||||
outShadowTouchPoint.set(SHADOW_SIZE / 2, SHADOW_SIZE / 2);
|
||||
outShadowSize.set(SHADOW_SIZE, SHADOW_SIZE)
|
||||
outShadowTouchPoint.set(SHADOW_SIZE / 2, SHADOW_SIZE / 2)
|
||||
}
|
||||
}
|
||||
|
||||
/** State containing information to start a drag for a widget. */
|
||||
/** State containing information to start a drag for a widget. */
|
||||
class DragState(
|
||||
private val widgetInfo: AppWidgetProviderInfo,
|
||||
private val dragShadowBuilder: DragShadowBuilder
|
||||
private val widgetInfo: WidgetInfo,
|
||||
private val dragShadowBuilder: DragShadowBuilder,
|
||||
) {
|
||||
private val uniqueId = UUID.randomUUID().toString()
|
||||
val pickerMimeType = "com.android.launcher3.widgetpicker.drag_and_drop/$uniqueId"
|
||||
|
||||
fun startDrag(view: View) {
|
||||
val clipData = ClipData(
|
||||
ClipDescription(
|
||||
// not displayed anywhere; so, set to empty.
|
||||
/* label= */ "",
|
||||
arrayOf(
|
||||
// unique picker specific mime type.
|
||||
pickerMimeType,
|
||||
// indicates that the clip item contains an intent (with extras about widget
|
||||
// info).
|
||||
ClipDescription.MIMETYPE_TEXT_INTENT
|
||||
)
|
||||
),
|
||||
ClipData.Item(
|
||||
Intent()
|
||||
.putExtra(Intent.EXTRA_USER, widgetInfo.profile)
|
||||
.putExtra(
|
||||
Intent.EXTRA_COMPONENT_NAME,
|
||||
widgetInfo.provider
|
||||
)
|
||||
val clipData =
|
||||
ClipData(
|
||||
ClipDescription(
|
||||
// not displayed anywhere; so, set to empty.
|
||||
/* label= */ "",
|
||||
arrayOf(
|
||||
// unique picker specific mime type.
|
||||
pickerMimeType,
|
||||
// indicates that the clip item contains an intent (with extras about widget
|
||||
// info).
|
||||
ClipDescription.MIMETYPE_TEXT_INTENT,
|
||||
),
|
||||
),
|
||||
ClipData.Item(
|
||||
when (widgetInfo) {
|
||||
is WidgetInfo.AppWidgetInfo ->
|
||||
buildIntentForClipData(
|
||||
user = widgetInfo.appWidgetProviderInfo.profile,
|
||||
componentName = widgetInfo.appWidgetProviderInfo.provider,
|
||||
)
|
||||
|
||||
is WidgetInfo.ShortcutInfo ->
|
||||
buildIntentForClipData(
|
||||
user = widgetInfo.launcherActivityInfo.user,
|
||||
componentName = widgetInfo.launcherActivityInfo.componentName,
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
view.startDragAndDrop(
|
||||
clipData,
|
||||
/*shadowBuilder=*/ dragShadowBuilder,
|
||||
/*myLocalState=*/ null,
|
||||
View.DRAG_FLAG_GLOBAL
|
||||
View.DRAG_FLAG_GLOBAL,
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildIntentForClipData(user: UserHandle, componentName: ComponentName): Intent =
|
||||
Intent()
|
||||
.putExtra(Intent.EXTRA_USER, user)
|
||||
.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
|
||||
}
|
||||
+54
-76
@@ -36,7 +36,6 @@ import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
@@ -90,61 +89,56 @@ fun WidgetDetails(
|
||||
) {
|
||||
val haptic = LocalHapticFeedback.current
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val contentDescription = stringResource(
|
||||
R.string.widget_details_accessibility_label,
|
||||
widget.label,
|
||||
widget.sizeInfo.spanX,
|
||||
widget.sizeInfo.spanY
|
||||
)
|
||||
val contentDescription =
|
||||
stringResource(
|
||||
R.string.widget_details_accessibility_label,
|
||||
widget.label,
|
||||
widget.sizeInfo.spanX,
|
||||
widget.sizeInfo.spanY,
|
||||
)
|
||||
|
||||
val detailsAlpha: Float by animateFloatAsState(
|
||||
targetValue = if (showAddButton) INVISIBLE_ALPHA else VISIBLE_ALPHA,
|
||||
animationSpec = tween(durationMillis = TOGGLE_ANIMATION_DURATION),
|
||||
label = "detailsAlphaAnimation"
|
||||
)
|
||||
val detailsAlpha: Float by
|
||||
animateFloatAsState(
|
||||
targetValue = if (showAddButton) INVISIBLE_ALPHA else VISIBLE_ALPHA,
|
||||
animationSpec = tween(durationMillis = TOGGLE_ANIMATION_DURATION),
|
||||
label = "detailsAlphaAnimation",
|
||||
)
|
||||
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.clickable(
|
||||
onClickLabel = if (showAddButton) {
|
||||
stringResource(R.string.widget_tap_to_hide_add_button_label)
|
||||
} else {
|
||||
stringResource(R.string.widget_tap_to_show_add_button_label)
|
||||
},
|
||||
interactionSource = interactionSource,
|
||||
indication = null
|
||||
) {
|
||||
haptic.performHapticFeedback(HapticFeedbackType.VirtualKey)
|
||||
onAddButtonToggle(
|
||||
widget.id
|
||||
)
|
||||
}
|
||||
.padding(
|
||||
horizontal = WidgetDetailsDimensions.horizontalPadding,
|
||||
vertical = WidgetDetailsDimensions.verticalPadding
|
||||
)
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxSize()
|
||||
.clickable(
|
||||
onClickLabel =
|
||||
if (showAddButton) {
|
||||
stringResource(R.string.widget_tap_to_hide_add_button_label)
|
||||
} else {
|
||||
stringResource(R.string.widget_tap_to_show_add_button_label)
|
||||
},
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
) {
|
||||
haptic.performHapticFeedback(HapticFeedbackType.VirtualKey)
|
||||
onAddButtonToggle(widget.id)
|
||||
}
|
||||
.padding(
|
||||
horizontal = WidgetDetailsDimensions.horizontalPadding,
|
||||
vertical = WidgetDetailsDimensions.verticalPadding,
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
modifier = Modifier
|
||||
.clearAndSetSemantics { this.contentDescription = contentDescription }
|
||||
.minimumInteractiveComponentSize()
|
||||
.graphicsLayer { alpha = detailsAlpha }
|
||||
.fillMaxSize()
|
||||
modifier =
|
||||
Modifier.clearAndSetSemantics { this.contentDescription = contentDescription }
|
||||
.minimumInteractiveComponentSize()
|
||||
.graphicsLayer { alpha = detailsAlpha }
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
WidgetLabel(
|
||||
label = widget.label,
|
||||
appIcon = appIcon,
|
||||
modifier = Modifier
|
||||
)
|
||||
WidgetLabel(label = widget.label, appIcon = appIcon, modifier = Modifier)
|
||||
if (showAllDetails) {
|
||||
WidgetSpanSizeLabel(
|
||||
spanX = widget.sizeInfo.spanX,
|
||||
spanY = widget.sizeInfo.spanY
|
||||
)
|
||||
WidgetSpanSizeLabel(spanX = widget.sizeInfo.spanX, spanY = widget.sizeInfo.spanY)
|
||||
widget.description?.let { WidgetDescription(it) }
|
||||
}
|
||||
}
|
||||
@@ -152,16 +146,12 @@ fun WidgetDetails(
|
||||
visible = showAddButton,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
enter = AddButtonDefaults.enterTransition,
|
||||
exit = AddButtonDefaults.exitTransition
|
||||
exit = AddButtonDefaults.exitTransition,
|
||||
) {
|
||||
AddButton(
|
||||
widget = widget,
|
||||
onClick = {
|
||||
onWidgetAddClick(
|
||||
WidgetInteractionInfo.WidgetAddInfo(
|
||||
widget.appWidgetProviderInfo
|
||||
)
|
||||
)
|
||||
onWidgetAddClick(WidgetInteractionInfo.WidgetAddInfo(widget.widgetInfo))
|
||||
haptic.performHapticFeedback(HapticFeedbackType.Confirm)
|
||||
},
|
||||
)
|
||||
@@ -170,33 +160,28 @@ fun WidgetDetails(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddButton(
|
||||
widget: PickableWidget,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
private fun AddButton(widget: PickableWidget, onClick: () -> Unit) {
|
||||
val accessibleDescription =
|
||||
stringResource(R.string.widget_tap_to_add_button_content_description, widget.label)
|
||||
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Button(
|
||||
modifier = Modifier.minimumInteractiveComponentSize(),
|
||||
contentPadding = AddButtonDimensions.paddingValues,
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = WidgetPickerTheme.colors.addButtonBackground,
|
||||
contentColor = WidgetPickerTheme.colors.addButtonContent
|
||||
),
|
||||
colors =
|
||||
ButtonDefaults.buttonColors(
|
||||
containerColor = WidgetPickerTheme.colors.addButtonBackground,
|
||||
contentColor = WidgetPickerTheme.colors.addButtonContent,
|
||||
),
|
||||
onClick = onClick,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Add,
|
||||
contentDescription = null // decorative
|
||||
contentDescription = null, // decorative
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.semantics { this.contentDescription = accessibleDescription },
|
||||
text = stringResource(R.string.widget_tap_to_add_button_label)
|
||||
text = stringResource(R.string.widget_tap_to_add_button_label),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -208,15 +193,13 @@ private fun WidgetLabel(label: String, appIcon: (@Composable () -> Unit)?, modif
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (appIcon != null) {
|
||||
appIcon()
|
||||
Spacer(
|
||||
modifier =
|
||||
Modifier
|
||||
.width(WidgetDetailsDimensions.appIconLabelSpacing)
|
||||
.fillMaxHeight()
|
||||
Modifier.width(WidgetDetailsDimensions.appIconLabelSpacing).fillMaxHeight()
|
||||
)
|
||||
}
|
||||
Text(
|
||||
@@ -272,12 +255,7 @@ private object WidgetDetailsDimensions {
|
||||
}
|
||||
|
||||
private object AddButtonDimensions {
|
||||
val paddingValues = PaddingValues(
|
||||
start = 8.dp,
|
||||
top = 11.dp,
|
||||
end = 16.dp,
|
||||
bottom = 11.dp
|
||||
)
|
||||
val paddingValues = PaddingValues(start = 8.dp, top = 11.dp, end = 16.dp, bottom = 11.dp)
|
||||
}
|
||||
|
||||
private object AddButtonDefaults {
|
||||
|
||||
+109
-107
@@ -63,8 +63,10 @@ import androidx.compose.ui.unit.coerceAtMost
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetId
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetInfo
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetPreview
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetSizeInfo
|
||||
import com.android.launcher3.widgetpicker.shared.model.isAppWidget
|
||||
import com.android.launcher3.widgetpicker.ui.WidgetInteractionInfo
|
||||
import com.android.launcher3.widgetpicker.ui.theme.WidgetPickerTheme
|
||||
import kotlin.math.roundToInt
|
||||
@@ -75,11 +77,11 @@ fun WidgetPreview(
|
||||
id: WidgetId,
|
||||
sizeInfo: WidgetSizeInfo,
|
||||
preview: WidgetPreview,
|
||||
appwidgetInfo: AppWidgetProviderInfo,
|
||||
widgetInfo: WidgetInfo,
|
||||
modifier: Modifier = Modifier,
|
||||
showDragShadow: Boolean,
|
||||
onWidgetInteraction: (WidgetInteractionInfo) -> Unit,
|
||||
onAddButtonToggle: (WidgetId) -> Unit
|
||||
onAddButtonToggle: (WidgetId) -> Unit,
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val haptic = LocalHapticFeedback.current
|
||||
@@ -93,16 +95,16 @@ fun WidgetPreview(
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.wrapContentSize()
|
||||
.clickable(
|
||||
modifier =
|
||||
modifier.wrapContentSize().clickable(
|
||||
interactionSource = interactionSource,
|
||||
// no ripples for preview taps that toggle the add button.
|
||||
indication = null
|
||||
indication = null,
|
||||
) {
|
||||
haptic.performHapticFeedback(HapticFeedbackType.VirtualKey)
|
||||
onAddButtonToggle(id)
|
||||
}) {
|
||||
}
|
||||
) {
|
||||
when (preview) {
|
||||
is WidgetPreview.PlaceholderWidgetPreview ->
|
||||
PlaceholderWidgetPreview(size = containerSize, widgetRadius = widgetRadius)
|
||||
@@ -112,30 +114,34 @@ fun WidgetPreview(
|
||||
bitmap = preview.bitmap,
|
||||
size = containerSize,
|
||||
widgetRadius = widgetRadius,
|
||||
widgetInfo = appwidgetInfo,
|
||||
widgetInfo = widgetInfo,
|
||||
showDragShadow = showDragShadow,
|
||||
onWidgetInteraction = onWidgetInteraction,
|
||||
)
|
||||
|
||||
is WidgetPreview.RemoteViewsWidgetPreview ->
|
||||
is WidgetPreview.RemoteViewsWidgetPreview -> {
|
||||
check(widgetInfo.isAppWidget())
|
||||
RemoteViewsWidgetPreview(
|
||||
remoteViews = preview.remoteViews,
|
||||
widgetInfo = appwidgetInfo,
|
||||
widgetInfo = widgetInfo,
|
||||
sizeInfo = sizeInfo,
|
||||
widgetRadius = widgetRadius,
|
||||
showDragShadow = showDragShadow,
|
||||
onWidgetInteraction = onWidgetInteraction,
|
||||
)
|
||||
}
|
||||
|
||||
is WidgetPreview.ProviderInfoWidgetPreview ->
|
||||
is WidgetPreview.ProviderInfoWidgetPreview -> {
|
||||
check(widgetInfo.isAppWidget())
|
||||
RemoteViewsWidgetPreview(
|
||||
previewLayoutProviderInfo = preview.providerInfo,
|
||||
widgetInfo = appwidgetInfo,
|
||||
widgetInfo = widgetInfo,
|
||||
sizeInfo = sizeInfo,
|
||||
widgetRadius = widgetRadius,
|
||||
showDragShadow = showDragShadow,
|
||||
onWidgetInteraction = onWidgetInteraction,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,8 +151,7 @@ private fun PlaceholderWidgetPreview(size: DpSize, widgetRadius: Dp) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier =
|
||||
Modifier
|
||||
.width(size.width)
|
||||
Modifier.width(size.width)
|
||||
.height(size.height)
|
||||
.background(
|
||||
color = WidgetPickerTheme.colors.widgetPlaceholderBackground,
|
||||
@@ -161,7 +166,7 @@ private fun PlaceholderWidgetPreview(size: DpSize, widgetRadius: Dp) {
|
||||
private fun BitmapWidgetPreview(
|
||||
bitmap: Bitmap,
|
||||
size: DpSize,
|
||||
widgetInfo: AppWidgetProviderInfo,
|
||||
widgetInfo: WidgetInfo,
|
||||
widgetRadius: Dp,
|
||||
showDragShadow: Boolean,
|
||||
onWidgetInteraction: (WidgetInteractionInfo) -> Unit,
|
||||
@@ -170,22 +175,24 @@ private fun BitmapWidgetPreview(
|
||||
val density = LocalDensity.current
|
||||
val haptic = LocalHapticFeedback.current
|
||||
|
||||
val scaledBitmapDimensions by remember(bitmap, density, size) {
|
||||
derivedStateOf { bitmap.calculateScaledDimensions(density, size, widgetRadius) }
|
||||
}
|
||||
val scaledBitmapDimensions by
|
||||
remember(bitmap, density, size) {
|
||||
derivedStateOf { bitmap.calculateScaledDimensions(density, size, widgetRadius) }
|
||||
}
|
||||
|
||||
val dragState by remember(widgetInfo, showDragShadow) {
|
||||
derivedStateOf {
|
||||
DragState(
|
||||
widgetInfo,
|
||||
if (showDragShadow) {
|
||||
ImageBitmapDragShadowBuilder(context, bitmap, scaledBitmapDimensions)
|
||||
} else {
|
||||
TransparentDragShadowBuilder
|
||||
}
|
||||
)
|
||||
val dragState by
|
||||
remember(widgetInfo, showDragShadow) {
|
||||
derivedStateOf {
|
||||
DragState(
|
||||
widgetInfo,
|
||||
if (showDragShadow) {
|
||||
ImageBitmapDragShadowBuilder(context, bitmap, scaledBitmapDimensions)
|
||||
} else {
|
||||
TransparentDragShadowBuilder
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var imagePositionInParent by remember { mutableStateOf(Offset.Zero) }
|
||||
|
||||
@@ -199,8 +206,7 @@ private fun BitmapWidgetPreview(
|
||||
contentDescription = null, // only visual (widget details provides the readable info)
|
||||
contentScale = ContentScale.FillBounds,
|
||||
modifier =
|
||||
Modifier
|
||||
.onGloballyPositioned { coordinates ->
|
||||
Modifier.onGloballyPositioned { coordinates ->
|
||||
imagePositionInParent = coordinates.positionInParent()
|
||||
}
|
||||
.pointerInput(bitmap) {
|
||||
@@ -215,21 +221,19 @@ private fun BitmapWidgetPreview(
|
||||
calculateImageDragBounds(
|
||||
scaledBitmapDimensions = scaledBitmapDimensions,
|
||||
imagePositionInParent = imagePositionInParent,
|
||||
offset = offset
|
||||
offset = offset,
|
||||
)
|
||||
onWidgetInteraction(
|
||||
WidgetInteractionInfo.WidgetDragInfo(
|
||||
mimeType = dragState.pickerMimeType,
|
||||
providerInfo = widgetInfo,
|
||||
widgetInfo = widgetInfo,
|
||||
bounds = bounds,
|
||||
widthPx = scaledBitmapDimensions.scaledSizePx.width,
|
||||
heightPx = scaledBitmapDimensions.scaledSizePx.height,
|
||||
previewInfo = WidgetPreview.BitmapWidgetPreview(
|
||||
bitmap = bitmap,
|
||||
),
|
||||
previewInfo = WidgetPreview.BitmapWidgetPreview(bitmap = bitmap),
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
.width(scaledBitmapDimensions.scaledSizeDp.width)
|
||||
@@ -242,26 +246,20 @@ private fun BitmapWidgetPreview(
|
||||
private fun calculateImageDragBounds(
|
||||
scaledBitmapDimensions: ImageScaledDimensions,
|
||||
imagePositionInParent: Offset,
|
||||
offset: Offset
|
||||
offset: Offset,
|
||||
): Rect {
|
||||
val bounds = Rect()
|
||||
bounds.left = 0
|
||||
bounds.top = 0
|
||||
bounds.right = scaledBitmapDimensions.scaledSizePx.width
|
||||
bounds.bottom = scaledBitmapDimensions.scaledSizePx.height
|
||||
val xOffset: Int =
|
||||
(imagePositionInParent.x - offset.x).roundToInt()
|
||||
val yOffset: Int =
|
||||
(imagePositionInParent.y - offset.y).roundToInt()
|
||||
val xOffset: Int = (imagePositionInParent.x - offset.x).roundToInt()
|
||||
val yOffset: Int = (imagePositionInParent.y - offset.y).roundToInt()
|
||||
bounds.offset(xOffset, yOffset)
|
||||
return bounds
|
||||
}
|
||||
|
||||
private fun Bitmap.calculateScaledDimensions(
|
||||
density: Density,
|
||||
size: DpSize,
|
||||
widgetRadius: Dp
|
||||
) =
|
||||
private fun Bitmap.calculateScaledDimensions(density: Density, size: DpSize, widgetRadius: Dp) =
|
||||
with(density) {
|
||||
val bitmapSize = DpSize(width = width.toDp(), height = height.toDp())
|
||||
val bitmapAspectRatio = bitmapSize.width / bitmapSize.height
|
||||
@@ -269,20 +267,20 @@ private fun Bitmap.calculateScaledDimensions(
|
||||
|
||||
// Scale by width if image has larger aspect ratio than the container else by
|
||||
// height; and avoid cropping the previews.
|
||||
val scale = if (bitmapAspectRatio > containerAspectRatio) {
|
||||
size.width / bitmapSize.width
|
||||
} else {
|
||||
size.height / bitmapSize.height
|
||||
}
|
||||
val scale =
|
||||
if (bitmapAspectRatio > containerAspectRatio) {
|
||||
size.width / bitmapSize.width
|
||||
} else {
|
||||
size.height / bitmapSize.height
|
||||
}
|
||||
|
||||
val scaledDpSize = DpSize(
|
||||
width = bitmapSize.width * scale,
|
||||
height = bitmapSize.height * scale
|
||||
)
|
||||
val scaledPxSize = IntSize(
|
||||
width = scaledDpSize.width.roundToPx(),
|
||||
height = scaledDpSize.height.roundToPx()
|
||||
)
|
||||
val scaledDpSize =
|
||||
DpSize(width = bitmapSize.width * scale, height = bitmapSize.height * scale)
|
||||
val scaledPxSize =
|
||||
IntSize(
|
||||
width = scaledDpSize.width.roundToPx(),
|
||||
height = scaledDpSize.height.roundToPx(),
|
||||
)
|
||||
val scaledRadius = (widgetRadius * scale).coerceAtMost(widgetRadius).value.roundToInt().dp
|
||||
|
||||
ImageScaledDimensions(
|
||||
@@ -290,7 +288,7 @@ private fun Bitmap.calculateScaledDimensions(
|
||||
scaledSizePx = scaledPxSize,
|
||||
scaledSizeDp = scaledDpSize,
|
||||
scaledRadiusDp = scaledRadius,
|
||||
scaledRadiusPx = scaledRadius.toPx()
|
||||
scaledRadiusPx = scaledRadius.toPx(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -298,7 +296,7 @@ private fun Bitmap.calculateScaledDimensions(
|
||||
private fun RemoteViewsWidgetPreview(
|
||||
remoteViews: RemoteViews? = null,
|
||||
previewLayoutProviderInfo: AppWidgetProviderInfo? = null,
|
||||
widgetInfo: AppWidgetProviderInfo,
|
||||
widgetInfo: WidgetInfo.AppWidgetInfo,
|
||||
sizeInfo: WidgetSizeInfo,
|
||||
widgetRadius: Dp,
|
||||
onWidgetInteraction: (WidgetInteractionInfo) -> Unit,
|
||||
@@ -308,67 +306,71 @@ private fun RemoteViewsWidgetPreview(
|
||||
val haptic = LocalHapticFeedback.current
|
||||
|
||||
val appWidgetHostView by
|
||||
remember(sizeInfo, widgetInfo) {
|
||||
derivedStateOf {
|
||||
WidgetPreviewHostView(context).apply {
|
||||
setContainerSizePx(
|
||||
IntSize(sizeInfo.containerWidthPx, sizeInfo.containerHeightPx)
|
||||
)
|
||||
remember(sizeInfo, widgetInfo) {
|
||||
derivedStateOf {
|
||||
WidgetPreviewHostView(context).apply {
|
||||
setContainerSizePx(
|
||||
IntSize(sizeInfo.containerWidthPx, sizeInfo.containerHeightPx)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val dragState by remember {
|
||||
derivedStateOf {
|
||||
DragState(
|
||||
widgetInfo = widgetInfo,
|
||||
dragShadowBuilder = if (showDragShadow) {
|
||||
DragShadowBuilder(appWidgetHostView)
|
||||
} else {
|
||||
TransparentDragShadowBuilder
|
||||
}
|
||||
dragShadowBuilder =
|
||||
if (showDragShadow) {
|
||||
DragShadowBuilder(appWidgetHostView)
|
||||
} else {
|
||||
TransparentDragShadowBuilder
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
key(appWidgetHostView) {
|
||||
AndroidView(
|
||||
modifier = Modifier
|
||||
.pointerInput(appWidgetHostView) {
|
||||
detectDragGesturesAfterLongPress(
|
||||
onDrag = { change, _ -> change.consume() },
|
||||
onDragStart = { offset ->
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
dragState.startDrag(appWidgetHostView)
|
||||
modifier =
|
||||
Modifier.pointerInput(appWidgetHostView) {
|
||||
detectDragGesturesAfterLongPress(
|
||||
onDrag = { change, _ -> change.consume() },
|
||||
onDragStart = { offset ->
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
dragState.startDrag(appWidgetHostView)
|
||||
|
||||
onWidgetInteraction(
|
||||
WidgetInteractionInfo.WidgetDragInfo(
|
||||
mimeType = dragState.pickerMimeType,
|
||||
providerInfo = widgetInfo,
|
||||
bounds = appWidgetHostView.getDragBoundsForOffset(offset),
|
||||
widthPx = appWidgetHostView.measuredWidth,
|
||||
heightPx = appWidgetHostView.measuredHeight,
|
||||
previewInfo = when {
|
||||
remoteViews != null ->
|
||||
WidgetPreview.RemoteViewsWidgetPreview(
|
||||
remoteViews = remoteViews,
|
||||
)
|
||||
onWidgetInteraction(
|
||||
WidgetInteractionInfo.WidgetDragInfo(
|
||||
mimeType = dragState.pickerMimeType,
|
||||
widgetInfo = widgetInfo,
|
||||
bounds = appWidgetHostView.getDragBoundsForOffset(offset),
|
||||
widthPx = appWidgetHostView.measuredWidth,
|
||||
heightPx = appWidgetHostView.measuredHeight,
|
||||
previewInfo =
|
||||
when {
|
||||
remoteViews != null ->
|
||||
WidgetPreview.RemoteViewsWidgetPreview(
|
||||
remoteViews = remoteViews
|
||||
)
|
||||
|
||||
previewLayoutProviderInfo != null ->
|
||||
WidgetPreview.ProviderInfoWidgetPreview(
|
||||
providerInfo = previewLayoutProviderInfo
|
||||
)
|
||||
previewLayoutProviderInfo != null ->
|
||||
WidgetPreview.ProviderInfoWidgetPreview(
|
||||
providerInfo = previewLayoutProviderInfo
|
||||
)
|
||||
|
||||
else ->
|
||||
throw IllegalStateException("No preview during drag")
|
||||
}
|
||||
else ->
|
||||
throw IllegalStateException(
|
||||
"No preview during drag"
|
||||
)
|
||||
},
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
.wrapContentSize()
|
||||
.clip(RoundedCornerShape(widgetRadius)),
|
||||
},
|
||||
)
|
||||
}
|
||||
.wrapContentSize()
|
||||
.clip(RoundedCornerShape(widgetRadius)),
|
||||
factory = { appWidgetHostView },
|
||||
update = { view ->
|
||||
// if preview.remoteViews is null, initial layout will render.
|
||||
@@ -376,7 +378,7 @@ private fun RemoteViewsWidgetPreview(
|
||||
// to be the previewLayout.
|
||||
view.setAppWidget(
|
||||
/*appWidgetId=*/ NO_OP_APP_WIDGET_ID,
|
||||
/*info=*/ previewLayoutProviderInfo ?: widgetInfo,
|
||||
/*info=*/ previewLayoutProviderInfo ?: widgetInfo.appWidgetProviderInfo,
|
||||
)
|
||||
view.updateAppWidget(remoteViews)
|
||||
},
|
||||
|
||||
+20
-21
@@ -61,10 +61,10 @@ import kotlin.math.max
|
||||
* @param appIcons optional map containing app icons to show in the widget details besides the label
|
||||
* (when showing the widgets outside of app context e.g. recommendations)
|
||||
* @param showDragShadow indicates if in a drag and drop session, widget picker should show drag
|
||||
* shadow containing the preview; if not set, a transparent shadow is rendered and host should
|
||||
* manage providing a shadow on its own.
|
||||
* shadow containing the preview; if not set, a transparent shadow is rendered and host should
|
||||
* manage providing a shadow on its own.
|
||||
* @param onWidgetInteraction callback invoked when a widget is being dragged and picker has started
|
||||
* global drag and drop session.
|
||||
* global drag and drop session.
|
||||
* @param modifier modifier with parent constraints and additional modifications
|
||||
*/
|
||||
@Composable
|
||||
@@ -93,12 +93,13 @@ fun WidgetsGrid(
|
||||
addButtonWidgetId = addButtonWidgetId,
|
||||
onWidgetInteraction = onWidgetInteraction,
|
||||
onAddButtonToggle = { id ->
|
||||
addButtonWidgetId = if (id != addButtonWidgetId) {
|
||||
id
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
addButtonWidgetId =
|
||||
if (id != addButtonWidgetId) {
|
||||
id
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -155,7 +156,7 @@ private fun WidgetsFlowRow(
|
||||
appIcons = appIcons,
|
||||
addButtonWidgetId = addButtonWidgetId,
|
||||
onWidgetInteraction = onWidgetInteraction,
|
||||
onAddButtonToggle = onAddButtonToggle
|
||||
onAddButtonToggle = onAddButtonToggle,
|
||||
)
|
||||
},
|
||||
previewContainerWidthPx = widgetSizeGroup.previewContainerWidthPx,
|
||||
@@ -184,21 +185,19 @@ private fun Previews(
|
||||
Box(
|
||||
contentAlignment = Alignment.BottomCenter,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.clearAndSetSemantics {
|
||||
traversalIndex = index.toFloat()
|
||||
testTag = buildWidgetPickerTestTag(WIDGET_PREVIEW_TEST_TAG)
|
||||
},
|
||||
Modifier.fillMaxSize().clearAndSetSemantics {
|
||||
traversalIndex = index.toFloat()
|
||||
testTag = buildWidgetPickerTestTag(WIDGET_PREVIEW_TEST_TAG)
|
||||
},
|
||||
) {
|
||||
WidgetPreview(
|
||||
id = widgetItem.id,
|
||||
sizeInfo = widgetItem.sizeInfo,
|
||||
preview = widgetPreview,
|
||||
appwidgetInfo = widgetItem.appWidgetProviderInfo,
|
||||
widgetInfo = widgetItem.widgetInfo,
|
||||
showDragShadow = showDragShadow,
|
||||
onWidgetInteraction = onWidgetInteraction,
|
||||
onAddButtonToggle = onAddButtonToggle
|
||||
onAddButtonToggle = onAddButtonToggle,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -211,7 +210,7 @@ private fun Details(
|
||||
addButtonWidgetId: WidgetId?,
|
||||
appIcons: Map<WidgetAppId, WidgetAppIcon>,
|
||||
onWidgetInteraction: (WidgetInteractionInfo) -> Unit,
|
||||
onAddButtonToggle: (WidgetId) -> Unit
|
||||
onAddButtonToggle: (WidgetId) -> Unit,
|
||||
) {
|
||||
widgets.forEachIndexed { index, widgetItem ->
|
||||
val appId = widgetItem.appId
|
||||
@@ -376,8 +375,8 @@ private fun Placeable.PlacementScope.placeRows(
|
||||
// Move to next row
|
||||
yPosition +=
|
||||
measuredRow.tallestPreviewHeight +
|
||||
measuredRow.tallestDetailsHeight +
|
||||
rowVerticalSpacingPx
|
||||
measuredRow.tallestDetailsHeight +
|
||||
rowVerticalSpacingPx
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -30,6 +30,7 @@ import androidx.compose.ui.unit.IntSize
|
||||
import com.android.launcher3.widgetpicker.shared.model.PickableWidget
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetAppId
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetId
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetInfo
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetPreview
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetSizeInfo
|
||||
import com.android.launcher3.widgetpicker.tests.R
|
||||
@@ -253,7 +254,7 @@ object WidgetsGridTestSamples {
|
||||
containerWidthPx = cellWidth,
|
||||
containerHeightPx = cellHeight,
|
||||
),
|
||||
appWidgetProviderInfo = newAppWidgetInfo("OneByOneProvider"),
|
||||
widgetInfo = WidgetInfo.AppWidgetInfo(newAppWidgetInfo("OneByOneProvider")),
|
||||
)
|
||||
|
||||
private fun twoByTwo(cellWidth: Int, cellHeight: Int) =
|
||||
@@ -262,7 +263,7 @@ object WidgetsGridTestSamples {
|
||||
appId = TEST_WIDGET_APP_ID,
|
||||
label = "Two by Two",
|
||||
description = null,
|
||||
appWidgetProviderInfo = newAppWidgetInfo("TwoByTwoProvider"),
|
||||
widgetInfo = WidgetInfo.AppWidgetInfo(newAppWidgetInfo("TwoByTwoProvider")),
|
||||
sizeInfo =
|
||||
WidgetSizeInfo(
|
||||
spanX = 2,
|
||||
@@ -283,7 +284,7 @@ object WidgetsGridTestSamples {
|
||||
appId = TEST_WIDGET_APP_ID,
|
||||
label = "Three by two",
|
||||
description = null,
|
||||
appWidgetProviderInfo = newAppWidgetInfo("threeByTwoProvider"),
|
||||
widgetInfo = WidgetInfo.AppWidgetInfo(newAppWidgetInfo("threeByTwoProvider")),
|
||||
sizeInfo =
|
||||
WidgetSizeInfo(
|
||||
spanX = 3,
|
||||
@@ -304,7 +305,7 @@ object WidgetsGridTestSamples {
|
||||
appId = TEST_WIDGET_APP_ID,
|
||||
label = "Four by two",
|
||||
description = null,
|
||||
appWidgetProviderInfo = newAppWidgetInfo("FourByTwoProvider"),
|
||||
widgetInfo = WidgetInfo.AppWidgetInfo(newAppWidgetInfo("FourByTwoProvider")),
|
||||
sizeInfo =
|
||||
WidgetSizeInfo(
|
||||
spanX = 4,
|
||||
|
||||
+8
-4
@@ -27,6 +27,7 @@ import com.android.launcher3.widgetpicker.shared.model.PickableWidget
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetApp
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetAppId
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetId
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetInfo
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetPreview
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetSizeInfo
|
||||
import com.android.launcher3.widgetpicker.shared.model.WidgetUserProfile
|
||||
@@ -118,10 +119,13 @@ object TestUtils {
|
||||
appId = finalWidgetAppId,
|
||||
label = providerClassName,
|
||||
description = null,
|
||||
appWidgetProviderInfo = AppWidgetProviderInfo().apply {
|
||||
widgetCategory = category
|
||||
provider = ComponentName.createRelative(PACKAGE_NAME, providerClassName)
|
||||
},
|
||||
widgetInfo =
|
||||
WidgetInfo.AppWidgetInfo(
|
||||
AppWidgetProviderInfo().apply {
|
||||
widgetCategory = category
|
||||
provider = ComponentName.createRelative(PACKAGE_NAME, providerClassName)
|
||||
}
|
||||
),
|
||||
sizeInfo =
|
||||
WidgetSizeInfo(
|
||||
spanX = 2,
|
||||
|
||||
+29
-24
@@ -53,11 +53,9 @@ import org.junit.runner.RunWith
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@AllowedDevices(allowed = [DeviceProduct.CF_PHONE])
|
||||
class WidgetInteractionsTest {
|
||||
@get:Rule
|
||||
val limitDevicesRule = LimitDevicesRule()
|
||||
@get:Rule val limitDevicesRule = LimitDevicesRule()
|
||||
|
||||
@get:Rule
|
||||
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
|
||||
@get:Rule val composeTestRule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test
|
||||
fun tapPreview_andClickAdd() {
|
||||
@@ -66,7 +64,8 @@ class WidgetInteractionsTest {
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
// tap on preview for widget 1
|
||||
composeTestRule.onAllNodesWithTag(PREVIEW_TEST_TAG)
|
||||
composeTestRule
|
||||
.onAllNodesWithTag(PREVIEW_TEST_TAG)
|
||||
.assertCountEquals(2)
|
||||
.onFirst()
|
||||
.performClick()
|
||||
@@ -74,18 +73,19 @@ class WidgetInteractionsTest {
|
||||
composeTestRule.waitForIdle()
|
||||
composeTestRule.onNodeWithText(WIDGET_ONE.label).isNotDisplayed() // label text not shown
|
||||
composeTestRule.onAllNodesWithText(ADD_BUTTON_TEXT).assertCountEquals(1)
|
||||
composeTestRule.onNodeWithContentDescription(WIDGET_ONE_ADD_BUTTON_CONTENT_DESC)
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(WIDGET_ONE_ADD_BUTTON_CONTENT_DESC)
|
||||
.assertExists()
|
||||
.performClick()
|
||||
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
// widget interaction callback invoked and correct provider info returned.
|
||||
composeTestRule.onNodeWithText(WIDGET_ONE.appWidgetProviderInfo.provider.toString())
|
||||
.assertExists()
|
||||
composeTestRule.onNodeWithText(WIDGET_ONE.widgetInfo.toString()).assertExists()
|
||||
|
||||
// tap again on preview for widget 1
|
||||
composeTestRule.onAllNodesWithTag(PREVIEW_TEST_TAG)
|
||||
composeTestRule
|
||||
.onAllNodesWithTag(PREVIEW_TEST_TAG)
|
||||
.assertCountEquals(2)
|
||||
.onFirst()
|
||||
.performClick()
|
||||
@@ -103,7 +103,8 @@ class WidgetInteractionsTest {
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
// tap on preview for widget 1
|
||||
composeTestRule.onAllNodesWithTag(PREVIEW_TEST_TAG)
|
||||
composeTestRule
|
||||
.onAllNodesWithTag(PREVIEW_TEST_TAG)
|
||||
.assertCountEquals(2)
|
||||
.onFirst()
|
||||
.performClick()
|
||||
@@ -114,11 +115,13 @@ class WidgetInteractionsTest {
|
||||
composeTestRule.onNodeWithText(WIDGET_ONE.label).isNotDisplayed()
|
||||
composeTestRule.onNodeWithText(WIDGET_TWO.label).isNotDisplayed()
|
||||
composeTestRule.onAllNodesWithText(ADD_BUTTON_TEXT).assertCountEquals(1)
|
||||
composeTestRule.onNodeWithContentDescription(WIDGET_ONE_ADD_BUTTON_CONTENT_DESC)
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(WIDGET_ONE_ADD_BUTTON_CONTENT_DESC)
|
||||
.assertExists()
|
||||
|
||||
// tap on preview for widget 2
|
||||
composeTestRule.onAllNodesWithTag(PREVIEW_TEST_TAG)
|
||||
composeTestRule
|
||||
.onAllNodesWithTag(PREVIEW_TEST_TAG)
|
||||
.assertCountEquals(2)
|
||||
.onLast()
|
||||
.performClick()
|
||||
@@ -129,11 +132,11 @@ class WidgetInteractionsTest {
|
||||
composeTestRule.onNodeWithText(WIDGET_ONE.label).isDisplayed()
|
||||
composeTestRule.onNodeWithText(WIDGET_TWO.label).isNotDisplayed()
|
||||
composeTestRule.onAllNodesWithText(ADD_BUTTON_TEXT).assertCountEquals(1)
|
||||
composeTestRule.onNodeWithContentDescription(WIDGET_TWO_ADD_BUTTON_CONTENT_DESC)
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(WIDGET_TWO_ADD_BUTTON_CONTENT_DESC)
|
||||
.assertExists()
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun TapToAddTestComposable() {
|
||||
var provider by remember { mutableStateOf("invalid") }
|
||||
@@ -149,7 +152,7 @@ class WidgetInteractionsTest {
|
||||
showDragShadow = false,
|
||||
onWidgetInteraction = { widgetInteractionInfo ->
|
||||
if (widgetInteractionInfo is WidgetInteractionInfo.WidgetAddInfo) {
|
||||
provider = widgetInteractionInfo.providerInfo.provider.toString()
|
||||
provider = widgetInteractionInfo.widgetInfo.toString()
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -160,16 +163,18 @@ class WidgetInteractionsTest {
|
||||
private val WIDGET_ONE = PERSONAL_TEST_APPS[0].widgets[0]
|
||||
private val WIDGET_TWO = PERSONAL_TEST_APPS[1].widgets[0]
|
||||
|
||||
private val TEST_WIDGET_GROUP = WidgetSizeGroup(
|
||||
previewContainerHeightPx = 200,
|
||||
previewContainerWidthPx = 200,
|
||||
widgets = listOf(WIDGET_ONE, WIDGET_TWO)
|
||||
)
|
||||
private val TEST_WIDGET_GROUP =
|
||||
WidgetSizeGroup(
|
||||
previewContainerHeightPx = 200,
|
||||
previewContainerWidthPx = 200,
|
||||
widgets = listOf(WIDGET_ONE, WIDGET_TWO),
|
||||
)
|
||||
|
||||
private val PREVIEWS = mapOf(
|
||||
WIDGET_ONE.id to TestUtils.createBitmapPreview(),
|
||||
WIDGET_TWO.id to TestUtils.createBitmapPreview()
|
||||
)
|
||||
private val PREVIEWS =
|
||||
mapOf(
|
||||
WIDGET_ONE.id to TestUtils.createBitmapPreview(),
|
||||
WIDGET_TWO.id to TestUtils.createBitmapPreview(),
|
||||
)
|
||||
|
||||
private val PREVIEW_TEST_TAG = buildWidgetPickerTestTag("widget_preview")
|
||||
private const val ADD_BUTTON_TEXT = "Add"
|
||||
|
||||
Reference in new issue
Block a user