1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.platform.uiautomator_helpers
18 
19 import android.graphics.Rect
20 import android.platform.uiautomator_helpers.DeviceHelpers.context
21 import android.platform.uiautomator_helpers.DeviceHelpers.uiDevice
22 import android.platform.uiautomator_helpers.SwipeUtils.calculateStartEndPoint
23 import android.platform.uiautomator_helpers.TracingUtils.trace
24 import android.util.TypedValue
25 import androidx.test.uiautomator.Direction
26 import androidx.test.uiautomator.UiObject2
27 import androidx.test.uiautomator.Until
28 import java.time.Duration
29 import java.time.temporal.ChronoUnit
30 
31 /**
32  * A fling utility that should be used instead of [UiObject2.fling] for more reliable flings.
33  *
34  * See [BetterSwipe] for more details on the problem of [UiObject2.fling].
35  */
36 object BetterFling {
37     private const val DEFAULT_FLING_MARGIN_DP = 30
38     private const val DEFAULT_PERCENTAGE = 1.0f
39     private val DEFAULT_FLING_DURATION = Duration.of(100, ChronoUnit.MILLIS)
40     private val DEFAULT_WAIT_TIMEOUT = Duration.of(5, ChronoUnit.SECONDS)
41 
42     /**
43      * Flings [percentage] of [rect] in the given [direction], with [marginDp] margins.
44      *
45      * Note that when direction is [Direction.DOWN], the scroll will be from the top to the bottom
46      * (to scroll down).
47      */
48     @JvmStatic
49     @JvmOverloads
flingnull50     fun fling(
51         rect: Rect,
52         direction: Direction,
53         duration: Duration = DEFAULT_FLING_DURATION,
54         marginDp: Int = DEFAULT_FLING_MARGIN_DP,
55         percentage: Float = DEFAULT_PERCENTAGE,
56     ) {
57         val (start, stop) =
58             calculateStartEndPoint(rect, direction, percentage, marginDp.dpToPx().toInt())
59 
60         trace("Fling $start -> $stop") {
61             uiDevice.performActionAndWait(
62                 { BetterSwipe.from(start).to(stop, duration).release() },
63                 Until.scrollFinished(Direction.reverse(direction)),
64                 DEFAULT_WAIT_TIMEOUT.toMillis()
65             )
66         }
67     }
68 
Numbernull69     private fun Number.dpToPx(): Float {
70         return TypedValue.applyDimension(
71             TypedValue.COMPLEX_UNIT_DIP,
72             toFloat(),
73             context.resources.displayMetrics,
74         )
75     }
76 }
77