Show border highlight when dragging display
In the process of adding highlight we use an extra feature of LayerDrawable to add insets so that we can add padding without changing the actual dimensions or position of the DisplayBlock Views. To make it easier to keep the values consistent and to aid in conversion between px and dp, use dimen values to store padding and highlight metrics. Bug: b/352650922 Flag: com.android.settings.flags.display_topology_pane_in_display_list Test: atest DisplayTopologyPreferenceTest.kt Change-Id: I51ff2ce4a086e84a0c529346f8ede90430090b11
This commit is contained in:
39
res/drawable/display_block_selection_marker_background.xml
Normal file
39
res/drawable/display_block_selection_marker_background.xml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Copyright (C) 2025 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.
|
||||||
|
~
|
||||||
|
-->
|
||||||
|
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<!-- Inner border -->
|
||||||
|
<item>
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<stroke
|
||||||
|
android:color="@color/display_topology_background_color"
|
||||||
|
android:width="@dimen/display_block_padding" />
|
||||||
|
<corners android:radius="@dimen/display_block_corner_radius" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<!-- Outer border -->
|
||||||
|
<item>
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<stroke
|
||||||
|
android:color="@color/system_secondary"
|
||||||
|
android:width="@dimen/display_block_highlight_width" />
|
||||||
|
<corners android:radius="@dimen/display_block_corner_radius" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
23
res/drawable/display_block_unselected_background.xml
Normal file
23
res/drawable/display_block_unselected_background.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Copyright (C) 2025 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.
|
||||||
|
~
|
||||||
|
-->
|
||||||
|
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
|
||||||
|
<stroke
|
||||||
|
android:color="@color/display_topology_background_color"
|
||||||
|
android:width="@dimen/display_block_padding" />
|
||||||
|
<corners android:radius="@dimen/display_block_corner_radius" />
|
||||||
|
</shape>
|
@@ -82,5 +82,6 @@
|
|||||||
|
|
||||||
<!-- Connected displays -->
|
<!-- Connected displays -->
|
||||||
<color name="display_topology_background_color">@color/settingslib_color_charcoal</color>
|
<color name="display_topology_background_color">@color/settingslib_color_charcoal</color>
|
||||||
|
<color name="system_secondary">@android:color/system_secondary_dark</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
|
@@ -224,4 +224,5 @@
|
|||||||
|
|
||||||
<!-- Connected displays -->
|
<!-- Connected displays -->
|
||||||
<color name="display_topology_background_color">@color/settingslib_color_grey100</color>
|
<color name="display_topology_background_color">@color/settingslib_color_grey100</color>
|
||||||
|
<color name="system_secondary">@android:color/system_secondary_light</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -550,4 +550,7 @@
|
|||||||
|
|
||||||
<!-- Connected displays -->
|
<!-- Connected displays -->
|
||||||
<dimen name="display_topology_pane_margin">24dp</dimen>
|
<dimen name="display_topology_pane_margin">24dp</dimen>
|
||||||
|
<dimen name="display_block_padding">5dp</dimen>
|
||||||
|
<dimen name="display_block_highlight_width">2dp</dimen>
|
||||||
|
<dimen name="display_block_corner_radius">10dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -20,12 +20,15 @@ import android.app.WallpaperManager
|
|||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.Point
|
import android.graphics.Point
|
||||||
import android.graphics.PointF
|
import android.graphics.PointF
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.graphics.drawable.LayerDrawable
|
||||||
import android.hardware.display.DisplayManager
|
import android.hardware.display.DisplayManager
|
||||||
import android.hardware.display.DisplayTopology
|
import android.hardware.display.DisplayTopology
|
||||||
import android.hardware.display.DisplayTopology.TreeNode.POSITION_BOTTOM
|
import android.hardware.display.DisplayTopology.TreeNode.POSITION_BOTTOM
|
||||||
@@ -168,36 +171,54 @@ class TopologyScale(
|
|||||||
|
|
||||||
const val TOPOLOGY_PREFERENCE_KEY = "display_topology_preference"
|
const val TOPOLOGY_PREFERENCE_KEY = "display_topology_preference"
|
||||||
|
|
||||||
/** Padding in pane coordinate pixels on each side of a display block. */
|
|
||||||
const val BLOCK_PADDING = 2f
|
|
||||||
|
|
||||||
/** Represents a draggable block in the topology pane. */
|
/** Represents a draggable block in the topology pane. */
|
||||||
class DisplayBlock(context : Context) : Button(context) {
|
class DisplayBlock(context : Context) : Button(context) {
|
||||||
|
@VisibleForTesting var mSelectedImage: Drawable = ColorDrawable(Color.BLACK)
|
||||||
|
@VisibleForTesting var mUnselectedImage: Drawable = ColorDrawable(Color.BLACK)
|
||||||
|
|
||||||
|
private val mSelectedBg = context.getDrawable(
|
||||||
|
R.drawable.display_block_selection_marker_background)!!
|
||||||
|
private val mUnselectedBg = context.getDrawable(
|
||||||
|
R.drawable.display_block_unselected_background)!!
|
||||||
|
private val mInsetPx = context.resources.getDimensionPixelSize(R.dimen.display_block_padding)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
isScrollContainer = false
|
isScrollContainer = false
|
||||||
isVerticalScrollBarEnabled = false
|
isVerticalScrollBarEnabled = false
|
||||||
isHorizontalScrollBarEnabled = false
|
isHorizontalScrollBarEnabled = false
|
||||||
|
|
||||||
|
// Prevents shadow from appearing around edge of button.
|
||||||
|
stateListAnimator = null
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets position of the block given unpadded coordinates. */
|
/** Sets position of the block given unpadded coordinates. */
|
||||||
fun place(topLeft: PointF) {
|
fun place(topLeft: PointF) {
|
||||||
x = topLeft.x + BLOCK_PADDING
|
x = topLeft.x
|
||||||
y = topLeft.y + BLOCK_PADDING
|
y = topLeft.y
|
||||||
}
|
}
|
||||||
|
|
||||||
val unpaddedX: Float
|
fun setWallpaper(wallpaper: Bitmap?) {
|
||||||
get() = x - BLOCK_PADDING
|
val wallpaperDrawable = BitmapDrawable(context.resources, wallpaper ?: return)
|
||||||
|
|
||||||
val unpaddedY: Float
|
fun framedBy(bg: Drawable): Drawable =
|
||||||
get() = y - BLOCK_PADDING
|
LayerDrawable(arrayOf(wallpaperDrawable, bg)).apply {
|
||||||
|
setLayerInsetRelative(0, mInsetPx, mInsetPx, mInsetPx, mInsetPx)
|
||||||
|
}
|
||||||
|
mSelectedImage = framedBy(mSelectedBg)
|
||||||
|
mUnselectedImage = framedBy(mUnselectedBg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setHighlighted(value: Boolean) {
|
||||||
|
background = if (value) mSelectedImage else mUnselectedImage
|
||||||
|
}
|
||||||
|
|
||||||
/** Sets position and size of the block given unpadded bounds. */
|
/** Sets position and size of the block given unpadded bounds. */
|
||||||
fun placeAndSize(bounds : RectF, scale : TopologyScale) {
|
fun placeAndSize(bounds : RectF, scale : TopologyScale) {
|
||||||
val topLeft = scale.displayToPaneCoor(bounds.left, bounds.top)
|
val topLeft = scale.displayToPaneCoor(bounds.left, bounds.top)
|
||||||
val bottomRight = scale.displayToPaneCoor(bounds.right, bounds.bottom)
|
val bottomRight = scale.displayToPaneCoor(bounds.right, bounds.bottom)
|
||||||
val layout = layoutParams
|
val layout = layoutParams
|
||||||
layout.width = (bottomRight.x - topLeft.x - BLOCK_PADDING * 2f).toInt()
|
layout.width = (bottomRight.x - topLeft.x).toInt()
|
||||||
layout.height = (bottomRight.y - topLeft.y - BLOCK_PADDING * 2f).toInt()
|
layout.height = (bottomRight.y - topLeft.y).toInt()
|
||||||
layoutParams = layout
|
layoutParams = layout
|
||||||
place(topLeft)
|
place(topLeft)
|
||||||
}
|
}
|
||||||
@@ -284,8 +305,8 @@ class DisplayTopologyPreference(context : Context)
|
|||||||
get() = displayManager.displayTopology
|
get() = displayManager.displayTopology
|
||||||
set(value) { displayManager.displayTopology = value }
|
set(value) { displayManager.displayTopology = value }
|
||||||
|
|
||||||
open val wallpaper : Drawable
|
open val wallpaper: Bitmap?
|
||||||
get() = WallpaperManager.getInstance(context).drawable ?: ColorDrawable(Color.BLACK)
|
get() = WallpaperManager.getInstance(context).bitmap
|
||||||
|
|
||||||
open fun registerTopologyListener(listener: Consumer<DisplayTopology>) {
|
open fun registerTopologyListener(listener: Consumer<DisplayTopology>) {
|
||||||
displayManager.registerTopologyListener(context.mainExecutor, listener)
|
displayManager.registerTopologyListener(context.mainExecutor, listener)
|
||||||
@@ -386,14 +407,20 @@ class DisplayTopologyPreference(context : Context)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wallpaperBitmap : Bitmap? = null
|
||||||
|
|
||||||
newBounds.forEach { (id, pos) ->
|
newBounds.forEach { (id, pos) ->
|
||||||
val block = recycleableBlocks.removeFirstOrNull() ?: DisplayBlock(context).apply {
|
val block = recycleableBlocks.removeFirstOrNull() ?: DisplayBlock(context).apply {
|
||||||
|
if (wallpaperBitmap == null) {
|
||||||
|
wallpaperBitmap = injector.wallpaper
|
||||||
|
}
|
||||||
// We need a separate wallpaper Drawable for each display block, since each needs to
|
// We need a separate wallpaper Drawable for each display block, since each needs to
|
||||||
// be drawn at a separate size.
|
// be drawn at a separate size.
|
||||||
background = injector.wallpaper
|
setWallpaper(wallpaperBitmap)
|
||||||
|
|
||||||
mPaneContent.addView(this)
|
mPaneContent.addView(this)
|
||||||
}
|
}
|
||||||
|
block.setHighlighted(false)
|
||||||
|
|
||||||
block.placeAndSize(pos, scaling)
|
block.placeAndSize(pos, scaling)
|
||||||
block.setOnTouchListener { view, ev ->
|
block.setOnTouchListener { view, ev ->
|
||||||
@@ -422,12 +449,15 @@ class DisplayTopologyPreference(context : Context)
|
|||||||
|
|
||||||
val stationaryDisps = positions.filter { it.first != displayId }
|
val stationaryDisps = positions.filter { it.first != displayId }
|
||||||
|
|
||||||
|
mDrag?.display?.setHighlighted(false)
|
||||||
|
block.setHighlighted(true)
|
||||||
|
|
||||||
// We have to use rawX and rawY for the coordinates since the view receiving the event is
|
// We have to use rawX and rawY for the coordinates since the view receiving the event is
|
||||||
// also the view that is moving. We need coordinates relative to something that isn't
|
// also the view that is moving. We need coordinates relative to something that isn't
|
||||||
// moving, and the raw coordinates are relative to the screen.
|
// moving, and the raw coordinates are relative to the screen.
|
||||||
mDrag = BlockDrag(
|
mDrag = BlockDrag(
|
||||||
stationaryDisps.toList(), block, displayId, displayPos.width(), displayPos.height(),
|
stationaryDisps.toList(), block, displayId, displayPos.width(), displayPos.height(),
|
||||||
ev.rawX - block.unpaddedX, ev.rawY - block.unpaddedY)
|
ev.rawX - block.x, ev.rawY - block.y)
|
||||||
|
|
||||||
// Prevents a container of this view from intercepting the touch events in the case the
|
// Prevents a container of this view from intercepting the touch events in the case the
|
||||||
// pointer moves outside of the display block or the pane.
|
// pointer moves outside of the display block or the pane.
|
||||||
@@ -454,9 +484,10 @@ class DisplayTopologyPreference(context : Context)
|
|||||||
val drag = mDrag ?: return false
|
val drag = mDrag ?: return false
|
||||||
val topology = mTopologyInfo ?: return false
|
val topology = mTopologyInfo ?: return false
|
||||||
mPaneContent.requestDisallowInterceptTouchEvent(false)
|
mPaneContent.requestDisallowInterceptTouchEvent(false)
|
||||||
|
drag.display.setHighlighted(false)
|
||||||
|
|
||||||
val newCoor = topology.scaling.paneToDisplayCoor(
|
val newCoor = topology.scaling.paneToDisplayCoor(
|
||||||
drag.display.unpaddedX, drag.display.unpaddedY)
|
drag.display.x, drag.display.y)
|
||||||
val newTopology = topology.topology.copy()
|
val newTopology = topology.topology.copy()
|
||||||
val newPositions = drag.stationaryDisps.map { (id, pos) -> id to PointF(pos.left, pos.top) }
|
val newPositions = drag.stationaryDisps.map { (id, pos) -> id to PointF(pos.left, pos.top) }
|
||||||
.plus(drag.displayId to newCoor)
|
.plus(drag.displayId to newCoor)
|
||||||
|
@@ -21,9 +21,9 @@ import android.hardware.display.DisplayTopology.TreeNode.POSITION_LEFT
|
|||||||
import android.hardware.display.DisplayTopology.TreeNode.POSITION_TOP
|
import android.hardware.display.DisplayTopology.TreeNode.POSITION_TOP
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.hardware.display.DisplayTopology
|
import android.hardware.display.DisplayTopology
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@@ -50,7 +50,8 @@ class DisplayTopologyPreferenceTest {
|
|||||||
val injector = TestInjector(context)
|
val injector = TestInjector(context)
|
||||||
val rootView = View.inflate(context, preference.layoutResource, /*parent=*/ null)
|
val rootView = View.inflate(context, preference.layoutResource, /*parent=*/ null)
|
||||||
val holder = PreferenceViewHolder.createInstanceForTests(rootView)
|
val holder = PreferenceViewHolder.createInstanceForTests(rootView)
|
||||||
val wallpaper = ColorDrawable(Color.MAGENTA)
|
val wallpaper = Bitmap.createBitmap(
|
||||||
|
intArrayOf(Color.MAGENTA), /*width=*/ 1, /*height=*/ 1, Bitmap.Config.RGB_565)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
preference.injector = injector
|
preference.injector = injector
|
||||||
@@ -60,14 +61,14 @@ class DisplayTopologyPreferenceTest {
|
|||||||
|
|
||||||
class TestInjector(context : Context) : DisplayTopologyPreference.Injector(context) {
|
class TestInjector(context : Context) : DisplayTopologyPreference.Injector(context) {
|
||||||
var topology: DisplayTopology? = null
|
var topology: DisplayTopology? = null
|
||||||
var systemWallpaper: Drawable? = null
|
var systemWallpaper: Bitmap? = null
|
||||||
var topologyListener: Consumer<DisplayTopology>? = null
|
var topologyListener: Consumer<DisplayTopology>? = null
|
||||||
|
|
||||||
override var displayTopology : DisplayTopology?
|
override var displayTopology : DisplayTopology?
|
||||||
get() = topology
|
get() = topology
|
||||||
set(value) { topology = value }
|
set(value) { topology = value }
|
||||||
|
|
||||||
override val wallpaper : Drawable
|
override val wallpaper: Bitmap?
|
||||||
get() = systemWallpaper!!
|
get() = systemWallpaper!!
|
||||||
|
|
||||||
override fun registerTopologyListener(listener: Consumer<DisplayTopology>) {
|
override fun registerTopologyListener(listener: Consumer<DisplayTopology>) {
|
||||||
@@ -164,14 +165,14 @@ class DisplayTopologyPreferenceTest {
|
|||||||
val (childBlock, rootBlock) = setupTwoDisplays()
|
val (childBlock, rootBlock) = setupTwoDisplays()
|
||||||
|
|
||||||
// After accounting for padding, child should be half the length of root in each dimension.
|
// After accounting for padding, child should be half the length of root in each dimension.
|
||||||
assertThat(childBlock.layoutParams.width + BLOCK_PADDING)
|
assertThat(childBlock.layoutParams.width)
|
||||||
.isEqualTo(rootBlock.layoutParams.width / 2)
|
.isEqualTo(rootBlock.layoutParams.width / 2)
|
||||||
assertThat(childBlock.layoutParams.height + BLOCK_PADDING)
|
assertThat(childBlock.layoutParams.height)
|
||||||
.isEqualTo(rootBlock.layoutParams.height / 2)
|
.isEqualTo(rootBlock.layoutParams.height / 2)
|
||||||
assertThat(childBlock.y).isGreaterThan(rootBlock.y)
|
assertThat(childBlock.y).isGreaterThan(rootBlock.y)
|
||||||
assertThat(childBlock.background).isEqualTo(wallpaper)
|
assertThat(childBlock.background).isEqualTo(childBlock.mUnselectedImage)
|
||||||
assertThat(rootBlock.background).isEqualTo(wallpaper)
|
assertThat(rootBlock.background).isEqualTo(rootBlock.mUnselectedImage)
|
||||||
assertThat(rootBlock.x - BLOCK_PADDING * 2)
|
assertThat(rootBlock.x)
|
||||||
.isEqualTo(childBlock.x + childBlock.layoutParams.width)
|
.isEqualTo(childBlock.x + childBlock.layoutParams.width)
|
||||||
|
|
||||||
assertThat(preference.mTopologyHint.text)
|
assertThat(preference.mTopologyHint.text)
|
||||||
@@ -180,7 +181,7 @@ class DisplayTopologyPreferenceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun dragDisplayDownward() {
|
fun dragDisplayDownward() {
|
||||||
val (leftBlock, rightBlock) = setupTwoDisplays()
|
val (leftBlock, _) = setupTwoDisplays()
|
||||||
|
|
||||||
val downEvent = MotionEventBuilder.newBuilder()
|
val downEvent = MotionEventBuilder.newBuilder()
|
||||||
.setPointer(0f, 0f)
|
.setPointer(0f, 0f)
|
||||||
@@ -191,7 +192,7 @@ class DisplayTopologyPreferenceTest {
|
|||||||
// coordinates. The original offset is 42, so the new offset will be 42 + 40.
|
// coordinates. The original offset is 42, so the new offset will be 42 + 40.
|
||||||
val moveEvent = MotionEventBuilder.newBuilder()
|
val moveEvent = MotionEventBuilder.newBuilder()
|
||||||
.setAction(MotionEvent.ACTION_MOVE)
|
.setAction(MotionEvent.ACTION_MOVE)
|
||||||
.setPointer(0f, leftBlock.layoutParams.height / 2f + BLOCK_PADDING)
|
.setPointer(0f, leftBlock.layoutParams.height / 2f)
|
||||||
.build()
|
.build()
|
||||||
val upEvent = MotionEventBuilder.newBuilder().setAction(MotionEvent.ACTION_UP).build()
|
val upEvent = MotionEventBuilder.newBuilder().setAction(MotionEvent.ACTION_UP).build()
|
||||||
|
|
||||||
@@ -220,7 +221,7 @@ class DisplayTopologyPreferenceTest {
|
|||||||
val moveEvent = MotionEventBuilder.newBuilder()
|
val moveEvent = MotionEventBuilder.newBuilder()
|
||||||
.setAction(MotionEvent.ACTION_MOVE)
|
.setAction(MotionEvent.ACTION_MOVE)
|
||||||
.setPointer(
|
.setPointer(
|
||||||
-leftBlock.layoutParams.width - 2f * BLOCK_PADDING,
|
-leftBlock.layoutParams.width.toFloat(),
|
||||||
-leftBlock.layoutParams.height / 2f)
|
-leftBlock.layoutParams.height / 2f)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@@ -278,8 +279,8 @@ class DisplayTopologyPreferenceTest {
|
|||||||
// Look for a display with the same unusual aspect ratio as the one we've added.
|
// Look for a display with the same unusual aspect ratio as the one we've added.
|
||||||
val expectedAspectRatio = 300f/320f
|
val expectedAspectRatio = 300f/320f
|
||||||
assertThat(paneChildren
|
assertThat(paneChildren
|
||||||
.map { (it.layoutParams.width.toFloat() + BLOCK_PADDING*2) /
|
.map { it.layoutParams.width.toFloat() /
|
||||||
(it.layoutParams.height.toFloat() + BLOCK_PADDING*2) }
|
it.layoutParams.height.toFloat() }
|
||||||
.filter { abs(it - expectedAspectRatio) < 0.001f }
|
.filter { abs(it - expectedAspectRatio) < 0.001f }
|
||||||
).hasSize(1)
|
).hasSize(1)
|
||||||
}
|
}
|
||||||
@@ -305,7 +306,7 @@ class DisplayTopologyPreferenceTest {
|
|||||||
assertThat(paneChildren).hasSize(1)
|
assertThat(paneChildren).hasSize(1)
|
||||||
val block = paneChildren[0]
|
val block = paneChildren[0]
|
||||||
|
|
||||||
val origY = block.unpaddedY
|
val origY = block.y
|
||||||
|
|
||||||
block.dispatchTouchEvent(MotionEventBuilder.newBuilder()
|
block.dispatchTouchEvent(MotionEventBuilder.newBuilder()
|
||||||
.setAction(MotionEvent.ACTION_DOWN)
|
.setAction(MotionEvent.ACTION_DOWN)
|
||||||
@@ -316,21 +317,21 @@ class DisplayTopologyPreferenceTest {
|
|||||||
.setPointer(0f, 30f)
|
.setPointer(0f, 30f)
|
||||||
.build())
|
.build())
|
||||||
|
|
||||||
assertThat(block.unpaddedY).isWithin(0.01f).of(origY)
|
assertThat(block.y).isWithin(0.01f).of(origY)
|
||||||
|
|
||||||
block.dispatchTouchEvent(MotionEventBuilder.newBuilder()
|
block.dispatchTouchEvent(MotionEventBuilder.newBuilder()
|
||||||
.setAction(MotionEvent.ACTION_UP)
|
.setAction(MotionEvent.ACTION_UP)
|
||||||
.build())
|
.build())
|
||||||
|
|
||||||
// Block should be back to original position.
|
// Block should be back to original position.
|
||||||
assertThat(block.unpaddedY).isWithin(0.01f).of(origY)
|
assertThat(block.y).isWithin(0.01f).of(origY)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun updatedTopologyCancelsDragIfNonTrivialChange() {
|
fun updatedTopologyCancelsDragIfNonTrivialChange() {
|
||||||
val (leftBlock, rightBlock) = setupTwoDisplays(POSITION_LEFT, /* childOffset= */ 42f)
|
val (leftBlock, _) = setupTwoDisplays(POSITION_LEFT, /* childOffset= */ 42f)
|
||||||
|
|
||||||
assertThat(leftBlock.unpaddedY).isWithin(0.01f).of(142.17f)
|
assertThat(leftBlock.y).isWithin(0.01f).of(142.17f)
|
||||||
|
|
||||||
leftBlock.dispatchTouchEvent(MotionEventBuilder.newBuilder()
|
leftBlock.dispatchTouchEvent(MotionEventBuilder.newBuilder()
|
||||||
.setAction(MotionEvent.ACTION_DOWN)
|
.setAction(MotionEvent.ACTION_DOWN)
|
||||||
@@ -340,29 +341,45 @@ class DisplayTopologyPreferenceTest {
|
|||||||
.setAction(MotionEvent.ACTION_MOVE)
|
.setAction(MotionEvent.ACTION_MOVE)
|
||||||
.setPointer(0f, 30f)
|
.setPointer(0f, 30f)
|
||||||
.build())
|
.build())
|
||||||
assertThat(leftBlock.unpaddedY).isWithin(0.01f).of(172.17f)
|
assertThat(leftBlock.y).isWithin(0.01f).of(172.17f)
|
||||||
|
|
||||||
// Offset is only different by 0.5 dp, so the drag will not cancel.
|
// Offset is only different by 0.5 dp, so the drag will not cancel.
|
||||||
injector.topology = twoDisplayTopology(POSITION_LEFT, /* childOffset= */ 41.5f)
|
injector.topology = twoDisplayTopology(POSITION_LEFT, /* childOffset= */ 41.5f)
|
||||||
injector.topologyListener!!.accept(injector.topology!!)
|
injector.topologyListener!!.accept(injector.topology!!)
|
||||||
|
|
||||||
assertThat(leftBlock.unpaddedY).isWithin(0.01f).of(172.17f)
|
assertThat(leftBlock.y).isWithin(0.01f).of(172.17f)
|
||||||
// Move block farther downward.
|
// Move block farther downward.
|
||||||
leftBlock.dispatchTouchEvent(MotionEventBuilder.newBuilder()
|
leftBlock.dispatchTouchEvent(MotionEventBuilder.newBuilder()
|
||||||
.setAction(MotionEvent.ACTION_MOVE)
|
.setAction(MotionEvent.ACTION_MOVE)
|
||||||
.setPointer(0f, 50f)
|
.setPointer(0f, 50f)
|
||||||
.build())
|
.build())
|
||||||
assertThat(leftBlock.unpaddedY).isWithin(0.01f).of(192.17f)
|
assertThat(leftBlock.y).isWithin(0.01f).of(192.17f)
|
||||||
|
|
||||||
injector.topology = twoDisplayTopology(POSITION_LEFT, /* childOffset= */ 20f)
|
injector.topology = twoDisplayTopology(POSITION_LEFT, /* childOffset= */ 20f)
|
||||||
injector.topologyListener!!.accept(injector.topology!!)
|
injector.topologyListener!!.accept(injector.topology!!)
|
||||||
|
|
||||||
assertThat(leftBlock.unpaddedY).isWithin(0.01f).of(125.67f)
|
assertThat(leftBlock.y).isWithin(0.01f).of(125.67f)
|
||||||
// Another move in the opposite direction should not move the left block.
|
// Another move in the opposite direction should not move the left block.
|
||||||
leftBlock.dispatchTouchEvent(MotionEventBuilder.newBuilder()
|
leftBlock.dispatchTouchEvent(MotionEventBuilder.newBuilder()
|
||||||
.setAction(MotionEvent.ACTION_MOVE)
|
.setAction(MotionEvent.ACTION_MOVE)
|
||||||
.setPointer(0f, -20f)
|
.setPointer(0f, -20f)
|
||||||
.build())
|
.build())
|
||||||
assertThat(leftBlock.unpaddedY).isWithin(0.01f).of(125.67f)
|
assertThat(leftBlock.y).isWithin(0.01f).of(125.67f)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun highlightDuringDrag() {
|
||||||
|
val (leftBlock, _) = setupTwoDisplays(POSITION_LEFT, /* childOffset= */ 42f)
|
||||||
|
|
||||||
|
assertThat(leftBlock.background).isEqualTo(leftBlock.mUnselectedImage)
|
||||||
|
leftBlock.dispatchTouchEvent(MotionEventBuilder.newBuilder()
|
||||||
|
.setAction(MotionEvent.ACTION_DOWN)
|
||||||
|
.setPointer(0f, 0f)
|
||||||
|
.build())
|
||||||
|
assertThat(leftBlock.background).isEqualTo(leftBlock.mSelectedImage)
|
||||||
|
leftBlock.dispatchTouchEvent(MotionEventBuilder.newBuilder()
|
||||||
|
.setAction(MotionEvent.ACTION_UP)
|
||||||
|
.build())
|
||||||
|
assertThat(leftBlock.background).isEqualTo(leftBlock.mUnselectedImage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user