# การดำเนินการขั้นสูงบนอินเทอร์เฟซ

บทนี้จะแนะนำเกี่ยวกับอินเทอร์เฟซอัตโนมัติที่ลึกซึ้งยิ่งขึ้น ซึ่งคุณสามารถใช้เพื่อดำเนินการที่ละเอียดอ่อนต่างๆ ได้ เนื้อหาในบทนี้ค่อนข้างเยอะ หากคุณเพิ่งเริ่มใช้งาน เราขอแนะนำให้อ่านแต่ละหัวข้ออย่างอดทน

```{tip}
เมื่อเขียนโค้ดอัตโนมัติ คุณสามารถป้อนคำสั่ง `lamda` ได้โดยตรงในเทอร์มินัลทางด้านขวาของเดสก์ท็อประยะไกล และทำการทดสอบโค้ดต่อไปนี้ หรือทำการทดสอบการเลือกองค์ประกอบหรือการคลิกด้วยตนเอง ซึ่งจะช่วยเพิ่มความเร็วในการเขียนและตรวจสอบโค้ดของคุณ
```

## การรับองค์ประกอบ (Get Element)

คุณอาจมีความเข้าใจเกี่ยวกับเรื่องนี้มาบ้างแล้วจากความรู้พื้นฐานหรือบทก่อนหน้า คุณต้องค้นหาองค์ประกอบที่เกี่ยวข้องผ่าน selector เพื่อที่จะดำเนินการกับมัน คุณน่าจะเห็นแล้วว่าสามารถรับพารามิเตอร์ selector ได้จากที่ไหน ตอนนี้เราจะแนะนำเพิ่มเติมเกี่ยวกับองค์ประกอบนี้ คุณสามารถเห็นข้อมูลที่เกี่ยวข้องกับองค์ประกอบ "同意" (ยอมรับ) ได้ทางด้านขวาของภาพนี้

![ตัวอย่างองค์ประกอบ](/assets/images/auto-eyeselect.png)

```{attention}
องค์ประกอบที่คุณคลิกโดยตรงบนอินเทอร์เฟซด้านซ้ายอาจไม่ใช่องค์ประกอบจริง เนื่องจากอาจมีขนาดและตำแหน่งทับซ้อนกับองค์ประกอบอื่น โดยปกติแล้ว หากมีองค์ประกอบหลายตัวที่มีขนาดและตำแหน่งทับซ้อนกัน เราจะแสดงไว้ในแถบข้อมูลด้านขวา คุณสามารถเลื่อนขึ้นลงเพื่อตรวจสอบว่าอันไหนคืออันที่คุณต้องการจริงๆ คุณยังสามารถกดปุ่ม TAB บนอินเทอร์เฟซการเลือกด้านซ้ายเพื่อวนดูองค์ประกอบทั้งหมดด้วยตนเอง
```

สำหรับองค์ประกอบข้างต้น โดยทั่วไปเราจะใช้ `text` ในการรับมัน เงื่อนไขในการใช้ `text` คือต้องไม่มีองค์ประกอบอื่นบนอินเทอร์เฟซปัจจุบันที่มี `text` เป็น "同意" (ยอมรับ) เช่นกัน นี่เป็นวิธีที่ง่ายที่สุด นอกจากนี้คุณยังสามารถเลือกใช้ `resourceId` ได้ แต่โปรดทราบว่า `resourceId` ในที่นี้ไม่ได้หมายถึง ID ที่ไม่ซ้ำกัน แต่หมายถึง ID ของทรัพยากร และในหนึ่งอินเทอร์เฟซอาจมีองค์ประกอบหลายตัวที่มี `resourceId` เดียวกัน ส่วนอื่นๆ เช่น `packageName`, `checkable` โดยทั่วไปไม่ค่อยได้ใช้ แต่ถ้าไม่มี `text`, `resourceId`, `description` ก็สามารถลองใช้ฟิลด์เหล่านี้ได้ เราสามารถรับองค์ประกอบนี้ได้หลายวิธีดังนี้

```python
element = d(text="同意")
element = d(text="同意", resourceId="com.tencent.news:id/btm_first_agree")
element = d(resourceId="com.tencent.news:id/btm_first_agree")
```

## การคลิกองค์ประกอบ (Element Click)

เรียกใช้อินเทอร์เฟซต่อไปนี้เพื่อทำการคลิกองค์ประกอบแบบธรรมดา ในบริบทนี้จะทำการคลิกปุ่ม "ยอมรับ" ด้วยตนเอง

```python
element.click()
```

หากคุณต้องการระบุตำแหน่งที่จะคลิกบนองค์ประกอบ คุณสามารถระบุ `corner` ได้ `Corner.COR_CENTER` หมายถึงการคลิกที่จุดศูนย์กลางขององค์ประกอบ คุณยังสามารถคลิกที่มุมซ้ายบนหรือมุมขวาล่าง (`COR_BOTTOMRIGHT`) ได้อีกด้วย

```python
element.click_exists(corner=Corner.COR_TOPLEFT)
```

ทำการกดค้าง (long press) บนองค์ประกอบนี้ หากไม่มีอยู่จะเกิด exception อินเทอร์เฟซนี้ยังรองรับ `corner` แต่ไม่สามารถระบุระยะเวลาการกดค้างได้

```python
element.long_click()
```

หากองค์ประกอบมีอยู่จะทำการคลิก หากไม่มีอยู่ การเรียกใช้อินเทอร์เฟซนี้จะไม่ทำให้เกิด exception อินเทอร์เฟซนี้ยังรองรับ `corner` เช่นกัน

```python
element.click_exists()
```

```python
>>> element.click_exists()
True
```

## การตรวจสอบว่ามีอยู่หรือไม่ (Check Existence)

ในหลายกรณี ก่อนที่จะดำเนินการต่อไป จำเป็นต้องตรวจสอบว่าองค์ประกอบนั้นมีอยู่หรือไม่ มิฉะนั้นกระบวนการถัดไปอาจเกิดข้อผิดพลาด หรืออาจเกิดการดำเนินการที่ผิดพลาดบนหน้าจอที่ไม่ถูกต้อง ในบางสถานการณ์คุณอาจต้องใช้อินเทอร์เฟซต่อไปนี้เพื่อตรวจสอบการมีอยู่ขององค์ประกอบ

```python
element.exists()
```

## ข้อมูลองค์ประกอบ (Element Information)

ในบางสถานการณ์ คุณอาจต้องการรับข้อมูลบางส่วนขององค์ประกอบ เช่น คุณอาจต้องการทราบพิกัด, พื้นที่, หรือข้อความหรือคำอธิบายที่อยู่บนองค์ประกอบ คุณสามารถใช้อินเทอร์เฟซต่อไปนี้เพื่ออ่านข้อมูลองค์ประกอบ

```python
element.info()
```

ในองค์ประกอบทดสอบของเราข้างต้น ข้อมูลที่ส่งออกจะเป็นดังนี้

```python
>>> info = element.info()
>>> print (info)
bounds { ... }
className: "android.widget.TextView"
clickable: true
enabled: true
focusable: true
packageName: "com.tencent.news"
resourceName: "com.tencent.news:id/btn_first_agree"
text: "\345\220\214\346\204\217"
visibleBounds { ... }
```

```{hint}
คุณอาจสังเกตเห็นว่าข้อมูลที่พิมพ์ออกมาข้างต้นขาดบางฟิลด์ไป เช่น `description` ซึ่งโดยปกติแล้วหมายความว่าฟิลด์นั้นมีค่าว่างหรือเป็น `false` คุณยังคงสามารถเข้าถึงฟิลด์ที่เกี่ยวข้องผ่าน property เพื่อรับค่าของมันได้ตามปกติ
```

จะเห็นได้ว่าข้อมูลนี้ค่อนข้างซับซ้อน ซึ่งเป็นรูปแบบการพิมพ์เริ่มต้นของ protobuf คุณสามารถเข้าถึง property ที่เกี่ยวข้องโดยตรงเพื่อพิมพ์ค่าจริงของมันออกมาได้ ตัวอย่างเช่น หากต้องการอ่านค่า `text` ขององค์ประกอบ คุณสามารถทำได้ดังนี้

```python
>>> info = element.info()
>>> print (info.text)
同意
```

แน่นอนว่ายังมีข้อมูลเกี่ยวกับพิกัดพื้นที่ขององค์ประกอบอยู่ด้วย คุณสามารถเข้าถึงข้อมูลเหล่านั้นได้เช่นกัน ตัวอย่างเช่น หากคุณต้องการรับข้อมูลพื้นที่ขององค์ประกอบ คุณสามารถทำได้ดังนี้เพื่อพิมพ์ข้อมูลพื้นที่ออกมา หรือจะเก็บไว้เป็นตัวแปรเพื่อใช้ในการดำเนินการต่อไปก็ได้

```python
>>> info = element.info()
>>> print (info.bounds)
```

ค่าที่ส่งออกหรือส่งคืนคือข้อมูลพื้นที่ (Bound) ซึ่งคุณจะพบในภายหลังว่าเป็นพารามิเตอร์ที่ใช้ในอินเทอร์เฟซการจับภาพหน้าจอบางตัวด้วย ใช่แล้ว คุณสามารถส่งพารามิเตอร์นี้ไปยังอินเทอร์เฟซการจับภาพหน้าจอเพื่อจับภาพเฉพาะองค์ประกอบนี้ได้ แต่เราได้ทำการ encapsulate ไว้ให้คุณแล้ว

คุณอาจต้องการรับความกว้างและความสูงขององค์ประกอบเพื่อคำนวณค่า offset บางอย่าง เช่น การคำนวณ offset ขององค์ประกอบอื่นที่สัมพันธ์กัน คุณสามารถทำได้ดังนี้
```python
>>> info = element.info()
>>> print (info.bounds.width, info.bounds.height)
484 138
```

หรือรับจุดศูนย์กลางขององค์ประกอบ หรือจุดมุม เช่น มุมซ้ายบนหรือมุมขวาล่าง โดยปกติแล้วอินเทอร์เฟซต่อไปนี้จะส่งคืนข้อมูล Point และคุณยังสามารถรับพิกัดแกน X, Y ของหน้าจออุปกรณ์จากอ็อบเจกต์ Point ได้อีกด้วย
```python
>>> info = element.info()
>>> print (info.bounds.center())
x: 792
y: 1908
>>> print (info.bounds.center().x)
792
```

การเรียกใช้ต่อไปนี้ใช้เพื่อรับพิกัดมุมขององค์ประกอบ ตัวอย่างนี้เป็นการรับพิกัดมุมซ้ายบนขององค์ประกอบ นอกจากนี้ยังรองรับการรับพิกัดของมุมทั้งสี่ เช่น `bottom-right`, `top-right`, `bottom-left`

```python
>>> info = element.info()
>>> print (info.bounds.corner("top-left"))
x: 550
y: 1839
>>> print (info.bounds.corner("top-left").x)
550
```

## การวนซ้ำองค์ประกอบ (Iterating Elements)

คุณยังสามารถวนซ้ำองค์ประกอบทั้งหมดที่ถูกเลือกโดย selector ได้ โดยปกติแล้วในบริบทนี้อาจมีองค์ประกอบเพียงตัวเดียว ดังนั้นหากคุณต้องการทดสอบ โปรดเลือก selector อื่นเพื่อทดสอบ คุณสามารถใช้ for loop หรือวิธีอื่นในการวนซ้ำบน selector ได้โดยตรง

```python
for i in element: print (i)
```

หรือหากคุณทราบว่ามีองค์ประกอบที่ตรงกันหลายตัว และต้องการรับองค์ประกอบที่ N ที่ตรงกัน คุณสามารถใช้อินเทอร์เฟซต่อไปนี้ได้

```python
element_3rd = element.get(3)
```

## การนับองค์ประกอบ (Counting Elements)

โดยปกติแล้วคุณจะไม่ค่อยได้ใช้อินเทอร์เฟซนี้โดยตรง การเรียกใช้ต่อไปนี้สามารถรับจำนวนองค์ประกอบที่ตรงกับ selector ปัจจุบันของคุณได้

```python
>>> element.count()
1
```

## การจับภาพหน้าจอองค์ประกอบ (Element Screenshot)

เรารองรับการจับภาพหน้าจอในระดับองค์ประกอบ คุณสามารถจับภาพเฉพาะขององค์ประกอบได้โดยไม่ต้องจับภาพทั้งหน้าจอแล้วมาตัดเอง

```python
element.screenshot(quality=60)
```

หลังจากจับภาพหน้าจอแล้ว คุณสามารถใช้ `getvalue` เพื่อรับข้อมูลไบนารีของภาพ หรือส่งต่อไปยัง PIL Image ได้โดยตรง

```python
>>> element.screenshot(quality=60).getvalue()
b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xe2\x02(ICC_PROFILE\x00\x01\x01\x00\x00\x02\x18\x00\x00\x00\x00\x02\x10\x00\x00mntrRGB XYZ \x00\x00...
```
หรือหากคุณไม่ต้องการประมวลผลต่อ คุณสามารถเลือกที่จะบันทึกภาพหน้าจอลงในไฟล์บนเครื่องได้โดยตรง
```python
>>> element.screenshot(quality=60).save("image.png")
```

## การรอองค์ประกอบ (Waiting for Element)

ในบางสถานการณ์ คุณอาจต้องตรวจสอบว่าหน้าปัจจุบันโหลดเสร็จแล้วหรือไม่ โดยปกติคุณสามารถตรวจสอบได้โดยการดูว่าองค์ประกอบที่เกี่ยวข้องปรากฏขึ้นแล้วหรือยัง เพื่อตัดสินว่าหน้าปัจจุบันโหลดเสร็จแล้วหรือไม่ ตัวอย่างต่อไปนี้จะรอให้องค์ประกอบ "同意" (ยอมรับ) ปรากฏขึ้น โดยมีเวลารอสูงสุด 10 วินาที

```{hint}
ระยะเวลาการรอที่นี่เป็นมิลลิวินาที ดังนั้น 10 วินาทีต้อง *1000, 10 วินาที = 10000 มิลลิวินาที
```

```python
element.wait_for_exists(10*1000)
```

```python
>>> element.wait_for_exists(10*1000)
True
```

แน่นอนว่าเราไม่เพียงแต่รองรับการรอให้องค์ประกอบปรากฏขึ้นเท่านั้น แต่ยังรองรับการรอให้องค์ประกอบหายไปอีกด้วย กล่าวคือ รอจนกว่าองค์ประกอบจะหายไปจากอินเทอร์เฟซ

```python
element.wait_until_gone(10*1000)
```

```python
>>> element.wait_until_gone(10*1000)
False
```

## การป้อนข้อความ (Text Input)

การป้อนข้อความเป็นส่วนที่ต้องให้ความสนใจเป็นพิเศษ เราไม่สามารถป้อนข้อความลงบนปุ่ม "ยอมรับ" ได้เพราะมันเป็นปุ่ม ตอนนี้เราจะยกตัวอย่างองค์ประกอบช่องป้อนข้อมูล (input box) ขึ้นมาใหม่ ข้อมูลพื้นฐานขององค์ประกอบนี้มีดังนี้

![การป้อนข้อความ](/assets/images/input-text.png)

```{attention}
การรับองค์ประกอบช่องป้อนข้อมูลอาจมีข้อควรระวังบางประการ โปรดใส่ใจเป็นพิเศษ เมื่อรับองค์ประกอบช่องป้อนข้อมูล แป้นพิมพ์ของคุณจะต้องอยู่ใน**สถานะแสดงผล**ก่อนจึงจะค้นหาองค์ประกอบที่เกี่ยวข้องได้ และแนะนำให้ค้นหาอย่างละเอียด มิฉะนั้นคุณอาจได้รับองค์ประกอบที่ไม่ใช่ช่องป้อนข้อมูลจริงๆ
```

```{hint}
ในกระบวนการของโค้ดอัตโนมัติ การทำให้แป้นพิมพ์อยู่ในสถานะแสดงผลนั้น เพียงแค่คุณเขียนโค้ดเพื่อคลิกที่ช่องป้อนข้อมูลที่แสดงอยู่ก่อนหน้านี้ก็เพียงพอแล้ว
```

สำหรับช่องป้อนข้อมูลข้างต้น เราสามารถเรียกใช้อินเทอร์เฟซต่อไปนี้เพื่อป้อนสตริง "你好世界" (สวัสดีชาวโลก) ลงในช่องป้อนข้อมูลได้ แน่นอนว่ายังรองรับการป้อนภาษาอังกฤษหรือสตริง unicode อื่นๆ ด้วย คุณเพียงแค่ทำตามตัวอย่างด้านล่างเพื่อป้อนข้อความในช่อง

```python
>>> element = d(text="搜索感兴趣的内容")
>>> element.set_text("你好世界")
True
```

หรือถ้าคุณเกิดความคิดอยากจะรับข้อความที่แสดงอยู่ในช่องป้อนข้อมูลปัจจุบัน คุณสามารถเรียกใช้ได้ดังนี้

```{attention}
คุณจะเห็นว่าเราเปลี่ยน selector ที่นี่ selector แรกของเราใช้ `text` แต่หลังจากที่เราป้อน `text` เข้าไป เนื้อหาก็เปลี่ยนไป ดังนั้นองค์ประกอบนั้นจึงไม่มีอยู่อีกต่อไป เราจึงเปลี่ยนไปใช้ selector อื่น การใช้ selector ที่เหมาะสมเป็นสิ่งสำคัญมาก แต่เราเขียนไปแล้วก็ปล่อยเลยตามเลย
```

```python
>>> element = d(className="android.widget.EditText")
>>> element.get_text()
'你好世界'
```

หรืออีกกรณีคือการล้างเนื้อหาที่ป้อนอยู่ในปัจจุบัน โดยปกติแล้วการป้อนข้อความจะล้างข้อความก่อนหน้าโดยอัตโนมัติ แต่คุณก็สามารถล้างด้วยตนเองได้

```{hint}
จริงๆ แล้วการใช้อินเทอร์เฟซการกดปุ่มซ้ำๆ เพื่อกดปุ่ม BACKSPACE ก็สามารถให้ผลลัพธ์ที่คล้ายกันได้
```

```python
>>> element = d(className="android.widget.EditText")
>>> element.clear_text_field( )
True
```

```{note}
ในกรณีสุดขั้ว บางแห่งอาจไม่สามารถใช้อินเทอร์เฟซนี้เพื่อป้อนข้อความได้ตามปกติ เรากำลังดำเนินการรองรับอยู่
```

## การปัดแบบธรรมดา (Normal Swipe)

ใช้อินเทอร์เฟซต่อไปนี้เพื่อทำการปัดหน้าจอ เช่น การเลื่อนรายการขึ้นลงเพื่อเปลี่ยนหน้า การเรียกใช้ต่อไปนี้เป็นการปัดขึ้น `step` สามารถปรับได้เอง ยิ่งค่ามากความเร็วในการปัดจะยิ่งช้าลง เหมาะสำหรับการปัดที่ต้องการความแม่นยำสูง

```{attention}
ในกรณีที่ต้องการความสะดวก การดำเนินการนี้ไม่จำเป็นต้องระบุพารามิเตอร์ selector แต่หากพบว่าไม่สามารถปัดได้ โปรดตั้งค่าเงื่อนไข selector เป็นองค์ประกอบที่เหมาะสมด้วยตนเอง เช่น องค์ประกอบที่มี `scrollable` หรือ container ระดับแรกของรายการ
```

```python
d().swipe(direction=Direction.DIR_UP, step=32)
```

```python
>>> element = d(resourceId="com.tencent.news:id/important_list_content")
>>> element.swipe(direction=Direction.DIR_UP, step=32)
True
```

| ตัวบ่งชี้ทิศทาง | คำอธิบาย |
|------------|----------|
| Direction.DIR_UP | ปัดขึ้น |
| Direction.DIR_LEFT | ปัดไปทางซ้าย |
| Direction.DIR_DOWN | ปัดลง |
| Direction.DIR_RIGHT | ปัดไปทางขวา |

## การปัดเร็ว (Fast Swipe / Fling)

การปัดเร็วคล้ายกับการที่คนปัดหน้าจออย่างรวดเร็ว การดำเนินการนี้จะปัดหน้าจออย่างรวดเร็ว เหมาะสำหรับการจำลองการกระทำเช่นการเรียกดูอย่างรวดเร็ว ตัวอย่างต่อไปนี้เป็นการปัดหน้าจอจากบนลงล่าง ในตัวอย่าง selector เป็นค่าว่าง คุณยังคงต้องพิจารณาว่าจะกรอก selector หรือไม่ตามสถานการณ์จริง

```python
d().fling_from_top_to_bottom()
```

การปัดเร็วจากล่างขึ้นบน

```python
d().fling_from_bottom_to_top()
```
การปัดเร็วจากซ้ายไปขวา
```python
d().fling_from_left_to_right()
```
การปัดเร็วจากขวาไปซ้าย

```python
d().fling_from_right_to_left()
```

```{attention}
ในกรณีที่ต้องการความสะดวก การดำเนินการนี้ไม่จำเป็นต้องระบุพารามิเตอร์ selector แต่หากพบว่าไม่สามารถปัดได้ โปรดตั้งค่าเงื่อนไข selector เป็นองค์ประกอบที่เหมาะสมด้วยตนเอง เช่น องค์ประกอบที่มี `scrollable` หรือ container ระดับแรกของรายการ
```

```python
>>> element = d(resourceId="com.tencent.news:id/important_list_content")
>>> element.fling_from_bottom_to_top()
True
```

กำลังปรับปรุง...

## การดำเนินการอื่นๆ

```python
# ลากแอปนี้ไปยังโฟลเดอร์ "ช็อปปิ้ง" (แก้ไขตามสถานการณ์จริง)
element.drag_to(Selector(text="购物"))

#########
# ค้นหาองค์ประกอบระดับเดียวกัน (sibling) หรือองค์ประกอบลูก (child)
#########
# บางครั้งอาจมีองค์ประกอบที่ซ้ำกันหรือไม่มีลักษณะเด่นชัด ทำให้ระบุตำแหน่งได้ยาก
# ในกรณีนี้ คุณสามารถจำกัดขอบเขตการค้นหาโดยการค้นหาองค์ประกอบลูก/องค์ประกอบระดับเดียวกัน
# องค์ประกอบลูก ตัวอย่างเช่น: ในกล่องล็อกอินการแชท ช่องป้อนข้อมูลจะเป็นองค์ประกอบลูกของกล่องล็อกอิน
# องค์ประกอบระดับเดียวกัน ตัวอย่างเช่น: ในช่องป้อนข้อมูลการแชท ช่องชื่อผู้ใช้และรหัสผ่านจะเป็นองค์ประกอบระดับเดียวกัน (ในสถานการณ์ปกติ)
form = d(resourceId="login_form")
form.child(index=1)
# นี่จะรับองค์ประกอบที่มี index เป็น 0 ภายใต้ login_form
form.child(index=1).sibling()
# คุณยังสามารถใช้วิธีนี้เพื่อค้นหาปุ่ม "กู้คืนรหัสผ่าน" ที่อยู่ระดับเดียวกับ login_form
# (จริงๆ แล้วสามารถตรวจสอบผ่านสตริงได้เลย ไม่จำเป็นต้องทำแบบนี้ นี่เป็นเพียงการสาธิต)
form.sibling(textContains="找回密码")
# พวกมันเองก็เป็น element คุณสามารถดำเนินการใดๆ กับมันได้เหมือนกับ element ทั่วไป


# อื่นๆ, ปัดลง/ซ้ายขวาบนไปเรื่อยๆ จนกว่าจะถึงด้านล่างสุด
# เนื่องจากไม่แน่เสมอไปว่าจะสามารถปัดจนสุดหรือตรวจจับได้ว่าปัดจนสุดแล้ว
# ดังนั้นพารามิเตอร์ max_swipes จึงเป็นสิ่งจำเป็น
d().fling_from_top_to_bottom_to_end(max_swipes=32)
d().fling_from_bottom_to_top_to_end(max_swipes=32)
d().fling_from_left_to_right_to_end(max_swipes=32)
d().fling_from_right_to_left_to_end(max_swipes=32)

#########
# scroll: การปัดแบบกลไก
#########
step = 60
max_swipes = 32
# ปัดจากบนลงล่าง step ขั้น
d().scroll_from_top_to_bottom(step)
# ปัดจากล่างขึ้นบน step ขั้น
d().scroll_from_bottom_to_top(step)
# ปัดจากซ้ายไปขวา step ขั้น
d().scroll_from_left_to_right(step)
# ปัดจากขวาไปซ้าย step ขั้น
d().scroll_from_right_to_left(step)

# อื่นๆ, ปัดลง/ซ้ายขวาบนไปเรื่อยๆ จนกว่าจะถึงด้านล่างสุด
# เหมือนกับคำอธิบาย fling ข้างต้น
d().scroll_from_top_to_bottom_to_end(max_swipes, step)
d().scroll_from_bottom_to_top_to_end(max_swipes, step)
d().scroll_from_left_to_right_to_end(max_swipes, step)
d().scroll_from_right_to_left_to_end(max_swipes, step)
```