wangzhibo
2026-08-16 ae6f40460dcd56af6c5f60ba52c883854c3bac55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
#!/usr/bin/env bash
set -euo pipefail
 
#
#    Copyright (C) 2024 ysfchn
#
#    This file is part of Gadgetbridge.
#
#    Gadgetbridge is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as published
#    by the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    Gadgetbridge is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
 
# Path of the ADB binary to use for ADB commands. If not set, "adb" will
# be used as the binary, meaning it will search from the PATH. Alternatively,
# if "ANDROID_HOME" variable exists, it will be used as fallback to search
# for the ADB binary.
ADB_BINARY="${ADB_BINARY:-"adb"}"
 
# Package name of Gadgetbridge. Wildcards are also allowed, in this
# case, will look for a matching package names. If there are more than
# 1 result, the script will not continue since we can't determine which
# Gadgetbridge flavor on the device should be used for screenshots.
#
# If there is only one Gadgetbridge flavor installed on the device, this
# variable can be kept this way, so it can launch whetever it is a Nightly 
# or not.
PACKAGE_NAME="${PACKAGE_NAME:-"nodomain.freeyourgadget.gadgetbridge.*"}"
 
# Additional commands that will be executed after disabling demo UI to revert
# the device in its original state (as in before running this script.)
REVERT_COMMANDS=""
 
# Where to save screenshots, relative to the folder 
# where this script is located. Don't prefix with "./".
OUTPUT_DIR="metadata/android/en-US/images/phoneScreenshots"
 
# Where to look for pngquant to optimize the taken screenshots,
# if pngquant is not available in given path, it will be ignored, so this is
# optional.
PNGQUANT_BIN="${PNGQUANT_BIN:-"pngquant"}"
 
# Displayed time on the screenshots in HH:MM format. It will be applied inside
# Android Demo Mode, so it won't change the actual Android system time.
#
# Set this value to "version" to automatically construct a time by reading 
# the current Gadgetbridge version code and converting it to minutes.
#
# SYSTEM_CLOCK="version"
SYSTEM_CLOCK="10:00"
 
# Set to 1 for debugging input actions, if set to "1", it won't run any 
# commands that causes any modifications (such as changing the theme, 
# enabling demo UI etc.) to the system.
DEBUG="${DEBUG:-0}"
 
# ---------------------------------------------------------------------
# Utility
# ---------------------------------------------------------------------
 
# Prefix output directory with current file path
OUTPUT_DIR="$(dirname $(realpath -s $0))/${OUTPUT_DIR}"
 
# Append semicolon for seperating new commands
if [ -n "${REVERT_COMMANDS}" ]; then
    REVERT_COMMANDS+=";" 
fi
 
# Read the version code of Gagdetbridge and convert it to HHMM format, 
# which will be set as the Android demo UI clock, 
# so it can hint which version of Gadgetbridge is being used 
# while taking screenshots.
#
# In some cases when the version code couldn't be read 
# or the constructed clock is invalid, it will fallback to 10:00.
get_clock() {
    if [ "${SYSTEM_CLOCK}" = "version" ]
    then
        # As how it is in build.gradle file.
        version_code=$(git rev-list HEAD --count)
        date --date "@${version_code}" -u +%M%S 2>/dev/null || echo "1000"
    else
        echo "${SYSTEM_CLOCK}" | sed "s/[^0-9]//g"
    fi
}
 
# Try to find the "adb" binary. First, look for "adb" specified in "ADB_BINARY" 
# variable. If variable is not specified, it will look for "adb" in PATH, 
# otherwise, check for "ANDROID_HOME" variable.
#
# It will print the path of the ADB binary to stdout if ADB is found, otherwise
# write to stderr and exit, since we can't continue without ADB.
locate_adb() {
    if [ ! -x "$(command -v ${ADB_BINARY})" ]
    then
        # Also try Android SDK environment variable.
        # https://developer.android.com/tools/variables#envar
        if [ -n "${ANDROID_HOME:-}" ]
        then
            if [ -x "${ANDROID_HOME}/platform-tools/adb" ]
            then
                echo "ADB found at: ${ANDROID_HOME}/platform-tools/adb" >&2
                echo "${ANDROID_HOME}/platform-tools/adb"
                return 0
            fi
        fi
        echo "ADB couldn't be found in '${ADB_BINARY}', exiting..." >&2
        exit 1
    else
        echo "ADB found at: ${ADB_BINARY}" >&2
        echo "${ADB_BINARY}"
        return 0
    fi
}
 
# Check if required packages are installed, and fail if its not.
check_deps() {
    if [ ! -x "$(command -v xmlstarlet)" ]
    then
        echo "'xmlstarlet' is an additional package that is required"
        echo "by this script. It may be available in your system package"
        echo "manager. Exiting..."
        exit 1
    fi
}
 
# Locate the ADB (see above function) and check for connected devices. If device
# count is 1, print the ADB path with a "-s DEVICE_SERIAL" suffix to stdout, so 
# we can make sure that ADB commands throughout the script will be executed only 
# for the specified device, serving as a safeguard.
# (e.g. "adb -s DEVICE_SERIAL shell echo Hello!")
#
# If deivce count is not equal to 1, exit and tell the reason to stderr.
get_adb_command() {
    adb_path=$(locate_adb)
    device_list="$("${adb_path}" devices | sed -n "s/^\([0-9a-zA-Z]*\)\s*device$/\1/p")"
    device_count="$(echo -n "${device_list}" | grep -c '^')"
    # Make sure there is an only single device connected.
    if [ $device_count != 1 ]
    then
        echo "" >&2
        echo "There must be an exactly only one device that connected to the ADB," >&2
        echo "but found $device_count devices instead! Exiting..." >&2
        exit 1
    fi
    # Construct a command line to be used in next commands.
    echo "${adb_path} -s ${device_list}"
}
 
# ---------------------------------------------------------------------
# ADB Commands
# ---------------------------------------------------------------------
 
# Changes the system theme programmatically.
# First it applies the light mode, and second, (if API level is >= 33),
# changes system colors.
set_system_theme() {
    adb_prefix="${1}"
 
    if [ "${DEBUG}" -eq "1" ]; then
        echo "Theme: Skipped because of debug"
        return
    fi
 
    # Read the existing night mode value on the system, save the original
    # value for reverting it later. And change system theme to light mode.
    # Run "adb shell cmd uimode night help" for reference.
    current_night_mode="$(${adb_prefix} shell cmd uimode night | sed "s/Night mode: //")"
    case "${current_night_mode}" in
        "no")
            echo "Theme: Already set to light, skipped"
            ;;
        "yes" | "auto" | "custom_bedtime" | "custom_schedule")
            REVERT_COMMANDS+="${adb_prefix} shell cmd uimode night ${current_night_mode} 1>/dev/null;"
            echo "Theme: Switching to light from dark"
            ${adb_prefix} shell cmd uimode night no 1>/dev/null
            ;;
        *)
            echo "Theme: Unsupported theme value, skipped"
    esac
 
    # For Android 13 and above, change the system colors programmatically to 
    # a red-ish color to better match with Gadgetbridge branding.
    if [ "$(${adb_prefix} shell getprop ro.build.version.sdk)" -ge "33" ]; then
        echo "Theme: Setting Dynamic color"
        # Reference for color properties:
        # https://source.android.com/docs/core/display/dynamic-color
        theme_properties='{
            "android.theme.customization.system_palette":"FFB2B5",
            "android.theme.customization.accent_color":"FFB2B5",
            "android.theme.customization.theme_style":"RAINBOW"
        }'
        original_theme="$(${adb_prefix} shell settings get secure theme_customization_overlay_packages | tr -d "[:space:]" | tr "\"" "\\\"")"
        echo "${original_theme}"
        # TODO: Reverting the colors currently doesn't work beause of the characters getting escaped somehow in ADB
        # REVERT_COMMANDS+="${adb_prefix} shell settings put secure theme_customization_overlay_packages ""'"${original_theme}"'"
        ${adb_prefix} shell settings put secure theme_customization_overlay_packages "'"$(echo "${theme_properties}" | tr -d "[:space:]" | tr "\"" "\\\"")"'"
    fi
}
 
# Enables Android Demo Mode and mock status bar icons.
#
# See the reference for list of properties used below:
# https://android.googlesource.com/platform/frameworks/base/+/master/packages/SystemUI/docs/demo_mode.md
#
# Note that the demo UI may a little bit problematic, e.g. when network 
# signal level is faked to 4, the signal level might be changed still
# if the actual signal level has changed even if demo UI is shown at the
# moment, so better to take screenshots quickly as possible before status
# icons are changed.
set_status_bar() {
    adb_prefix="${1}"
 
    if [ "${DEBUG}" -eq "1" ]; then
        echo "Demo mode: Skipped because of debug"
        return
    fi
 
    echo "Demo mode: Enabling"
    ${adb_prefix} shell settings put global sysui_demo_allowed 1
 
    echo "Demo mode: Setting status icons"
 
    # Enter the demo mode
    ${adb_prefix} shell am broadcast -a com.android.systemui.demo --es command enter 1>/dev/null
 
    # Wi-Fi and mobile data for each SIM slot
    # (couldn't manage to turn off the second slot for dual SIM)
    ${adb_prefix} shell am broadcast -a com.android.systemui.demo --es command network \
        --es wifi show --es level 4 1>/dev/null
    ${adb_prefix} shell am broadcast -a com.android.systemui.demo --es command network \
        --es mobile show --es level 4 1>/dev/null
 
    # Battery
    ${adb_prefix} shell am broadcast -a com.android.systemui.demo --es command battery \
        --es level 100 --es plugged false --es powersave false 1>/dev/null
 
    # Explictly disable status icons
    ${adb_prefix} shell am broadcast -a com.android.systemui.demo --es command status \
        --es volume hide \
        --es bluetooth hide \
        --es location hide \
        --es alarm hide \
        --es sync hide \
        --es tty hide \
        --es eri hide \
        --es mute hide \
        --es speakerphone hide 1>/dev/null
 
    # No notification icons
    ${adb_prefix} shell am broadcast -a com.android.systemui.demo --es command notifications \
        --es visible false 1>/dev/null
 
    # Set system clock
    ${adb_prefix} shell am broadcast -a com.android.systemui.demo --es command clock \
        --es hhmm $(get_clock) 1>/dev/null
 
    # Also disable demo UI itself when reverting the preferences.
    REVERT_COMMANDS+="${adb_prefix} shell am broadcast -a com.android.systemui.demo --es command exit 1>/dev/null;"
    REVERT_COMMANDS+="${adb_prefix} shell settings put global sysui_demo_allowed 0;"
}
 
# In addition to the Android Demo Mode (as above), we can mock more system
# status icons with LineageOS (or ROMs that based on LineageOS) specific 
# settings, so we can keep the AOSP-like look for screenshots.
#
# This function will be executed for all devices regardless of their ROM,
# since this function already checks if running under LineageOS, and if it
# is not, it is a no-op.
set_status_bar_lineage_os() {
    adb_prefix="${1}"
 
    if [ "${DEBUG}" -eq "1" ]; then
        echo "LineageOS: Skipped because of debug"
        return
    fi
 
    # As its name implies, "ro.lineage.device" property is found on
    # LineageOS (or based on it) ROMs, so we check for its existence.
    if [ -n "$(${adb_prefix} shell getprop ro.lineage.device)" ]
    then
        echo "LineageOS: Setting ROM-specific icons"
 
        # LineageOS allows changing the battery icon style, so we set it
        # to portrait and hide the battery percent temporarily. Note the
        # "--lineage" flag here, since it is a LineageOS addition, not
        # Android's, so it won't work without that flag.
        #
        # See also for all system properties:
        # https://lineageos.github.io/android_lineage-sdk/reference/lineageos/providers/LineageSettings.System.html
 
        battery_style="$(${adb_prefix} shell settings get --lineage system status_bar_battery_style)"
        battery_percent="$(${adb_prefix} shell settings get --lineage system status_bar_show_battery_percent)"
 
        # Save the original values to a variable, so we can execute these commands after
        # screenshots are taken to revert the changed system preferences to its original state.
        REVERT_COMMANDS+="${adb_prefix} shell settings put --lineage system status_bar_battery_style ${battery_style};"
        REVERT_COMMANDS+="${adb_prefix} shell settings put --lineage system status_bar_show_battery_percent ${battery_percent};"
 
        ${adb_prefix} shell settings put --lineage system status_bar_battery_style 0
        ${adb_prefix} shell settings put --lineage system status_bar_show_battery_percent 0
    else
        echo "LineageOS: Not a LineageOS ROM, skipped"
    fi
}
 
# Filters the installed packages on the device and prints the matched
# Gadgetbridge package name that specified in "PACKAGE_NAME" variable.
get_gadgetbridge_app() {
    adb_prefix="${1}"
 
    # Convert the "PACKAGE_NAME" variable to a valid regex format, change periods to
    # literal periods, and change wildcards to a valid wildcards.
    package_filter="$(echo "${PACKAGE_NAME}" | tr "." "\\." | tr "*" ".*")"
 
    # List installed & enabled ("-e" flag) packages, and find the packages that
    # matches with the pattern.
    packages="$(${adb_prefix} shell cmd package list packages -e | sed -rn "s/^package:(${package_filter})/\1/p")"
    package_count="$(echo -n "${packages}" | grep -c '^')"
 
    # There can be multiple Gadgetbridge flavors installed on the device, since we
    # can't determine which one should be launched, we exit.
    if [ $package_count != 1 ]
    then
        echo "" >&2
        echo "There are none or more than one Gadgetbridge flavors found on the device," >&2
        echo "since it can't be determined which Gadgetbridge flavor should be used, " >&2
        echo "you will need to specifically set the preferred flavor with PACKAGE_NAME" >&2
        echo "variable to continue!" >&2
        exit 1
    fi
 
    echo "Chosen app: ${packages}" >&2
    echo "${packages}"
}
 
# ---------------------------------------------------------------------
# Functions
# ---------------------------------------------------------------------
 
optimize_screenshots() {
    if [ -x "$(command -v ${PNGQUANT_BIN})" ]
    then
        echo "Optimize: Running pngquant"
        "$(command -v ${PNGQUANT_BIN})" --strip --force --ext .png $(find "${OUTPUT_DIR}" -name "*.png")
        echo "Optimize: Done"
    else
        echo "Optimize: Couldn't find pngquant at '${PNGQUANT_BIN}', skipped"
    fi
}
 
# Takes a screenshot of the screen and saves to a given name. Optionally,
# it also can be prompted to pressing a key before taking a screenshot.
grab_screenshot() {
    adb_prefix="${1}"
    file_name="${2}"
    wait_for_key_prompt="${3:-}"
 
    if [ -n "${wait_for_key_prompt}" ]; then
        read -s -r -N 1 -p "[screenshot]: Waiting for '${file_name}' - ${wait_for_key_prompt} ** [Press any key to continue]"
        echo ""
    fi
    ${adb_prefix} shell screencap -p > "${OUTPUT_DIR}/${file_name}"
    # Play a bell sound to indicate the screenshot has been saved.
    echo -en '\a'
    echo "[screenshot]: Took '${file_name}'."
}
 
# Gets the XY touch positions of a UI element that hinted by a specific
# name by filtering from the layout data. The layout can be obtained by 
# "dump_screen_layout" function. 
get_position() {
    dump_output="${1}"
    name="${2}"
 
    # There can be multiple elements that matching the same "name", so
    # we limit to printing one line only by specifying which n-th element
    # to return. If not given, will print the coordinates of the first
    # matching (1st) element.
    index="${3:-1}"
    echo "${dump_output}" | sed -rn "s/name:${name},position:([0-9]+):([0-9]+)/\1 \2/p" | sed -n ${index}p
}
 
launch_gadgetbridge() {
    adb_prefix="${1}"
    package_name="${2}"
 
    # Check if Gadgetbridge is launched already, and warn the user to close
    # Gadgetbridge, so we can start in the home screen when we launch
    # Gadgetbridge.
    status=$(${adb_prefix} shell dumpsys window windows \
        | grep -E 'mCurrentFocus|mFocusedApp|mInputMethodTarget|mSurface' \
        | grep -qs "${package_name}" || echo $?)
    
    if [ -z $status ]; then
        echo "[!] Gadgetbridge is currently shown on the device. Close it " >&2
        echo "(and delete it from the recents) and press any key to continue." >&2
        read -s -r -N 1
        launch_gadgetbridge "${adb_prefix}" "${package_name}"
    else
        echo "Launching Gadgetbridge..."
 
        # Launch Gadgetbridge with monkey command, since it doesn't require us
        # to specify the activity class name along with the package name.
        ${adb_prefix} shell monkey -p "${package_name}" 1 1 1>/dev/null 2>&1
 
        # Give it some time before continuing.
        sleep 2
    fi
}
 
# Creates a dump of the current layout that is shown on the device screen.
# So this allow us to programmatically get touch positions of UI elements.
# 
# It will output a string that contains element descriptions and their
# X/Y positions, for each valid element:
# "name:Open navigation drawer,position:70:159"
dump_screen_layout() {
    adb_prefix="${1}"
 
    # "uiautomator" service prints a XML document, but it also prints a 
    # "UI hierchary dumped to..." message in the end, so we strip this
    # message from the string to make it a valid XML text.
    char_count=$(echo "UI hierchary dumped to: /dev/tty" | wc -c)
    xml_document="$(${adb_prefix} exec-out uiautomator dump /dev/tty | head -c -${char_count})"
 
    # XPath to list UI nodes listed in the current screen (we specifically
    # only list nodes in the Gadgetbridge app to be explicit), and print
    # their bound positions ("bounds") and UI names ("content-desc"), so we
    # can filter elements by their names.
    package_name="$(echo "${PACKAGE_NAME}" | sed "s/*$//")"
    xml_path="./hierarchy/node[contains(@package, \"${package_name}\") and @class=\"android.widget.FrameLayout\"]//node/@*[name() = \"bounds\" or name() = \"content-desc\"]"
 
    index=0
    previous_ui_name=""
    while IFS="" read -r line || [ -n "$line" ]
    do
        # The output from XPath query, we will have attribute values printed
        # one by one, so since we look for both "bounds" and "content-desc" attribute,
        # it will print:
        # - bounds
        # - content-desc
        # - bounds
        # - content-desc
        #
        # So to group each node, we check if it is divisible by 2, the count of the
        # attributes that we are listing.
        if [ "$(( $index % 2 ))" -eq 0 ]; then
            # This will be the "content-desc", so we pass it as-is.
            previous_ui_name="${line}"
        else
            # This will be the "bounds", so we convert this value to a X/Y positions.
            # Each bound value will contain 4 points, START_X, START_Y, END_X and END_Y,
            # and is formatted as: "[0,0][1080,2219]", since we only need a single position
            # to touch it, we calculate the center of both X and Y here.
            #
            # To read the values, we need to convert these bounds to a Bash array,
            # so remove all comma and square bracket characters to only leave the numbers.
            # "[0,0][1080,2219]" -> array of 4: "0 0 1080 2219"
            bounds=($(echo "${line}" | sed "s/^\[//; s/\[/ /g; s/\]//g; s/,/ /g"))
 
            # Now we can calculate the center position of the element.
            # X_CENTER = X_START + ((X_END - X_START) / 2)
            # Y_CENTER = Y_START + ((Y_END - Y_START) / 2)
            center_x=$((( ${bounds[0]} + ((${bounds[2]} - ${bounds[0]}) / 2) )))
            center_y=$((( ${bounds[1]} + ((${bounds[3]} - ${bounds[1]}) / 2) )))
 
            # Only output if there is a UI name for the element.
            if [ -n "${previous_ui_name}" ]
            then
                echo "name:${previous_ui_name},position:${center_x}:${center_y}"
            fi
        fi
        index=$(( index + 1 ))
    done < <(echo "${xml_document}" | xmlstarlet sel --template --value-of "${xml_path}")
    if [ $index -eq 0 ]
    then
        echo "[!] Couldn't find valid elements on the screen, is Gadgetbridge currently shown on the screen?" >&2
    fi
}
 
perform_input() {
    adb_prefix="${1}"
    input_type="${2}"
 
    case "${input_type}" in
        # Swipe for a specific direction.
        "swipe_left" | "swipe_right")
            # Get screen dimensions.
            dimensions="$(${adb_prefix} shell wm size | rev | cut -d " " -f 1 | rev)"
            screen_x=$(echo "$dimensions" | cut -d "x" -f 1)
            screen_y=$(echo "$dimensions" | cut -d "x" -f 2)
            # If swiping to the left, swipe to the left edge of the screen.
            swipe_x=0
            # Or if swiping to the right, swipe to the right edge of the screen.
            if [ "${input_type}" = "swipe_right" ]; then
                swipe_x=$screen_x
            fi
            # Perform the swipe (starting form the center of the screen) 
            # to given direction in 100 milliseconds.
            ${adb_prefix} shell input swipe $((screen_x / 2)) $((screen_y / 2)) $swipe_x $((screen_y / 2)) 100
            sleep 1
            ;;
        # Send "back" button key.
        "go_back")
            # 111 = KEYCODE_ESCAPE
            # 4 = KEYCODE_BACK
            ${adb_prefix} shell input keyevent 4
            sleep 0.5
            ;;
        # Touch to a UI element which hinted with a given element name.
        "touch_element")
            element_name="${3}"
            xml_layout="$(dump_screen_layout "${adb_prefix}")"
            element_index="${4:-1}"
            # Get the X/Y position of given element name, and touch it.
            ${adb_prefix} shell input tap $(get_position "${xml_layout}" "${element_name}" "${element_index}")
            sleep 0.5
            ;;
        *)
            echo "[!] Unsupported input type!"
            exit 1
    esac
}
 
# Open Gadgetbridge and start taking screenshots of different screens.
# To find out the touch positions;
# Developer Options > Under "Input" category > Enable "Pointer location"
#
# TODO: Calculate the exact position from screen density and dimensions to make X/Y positions adaptive with other screens.
take_screenshots() {
    adb_prefix="${1}"
 
    echo; echo "! - If system preferences are not reverted automatically after exiting, you can manually run these commands:"
    echo "${REVERT_COMMANDS}"
    echo; echo "Taking screenshots, it might take some time;"
 
    # Dashboard
    perform_input "${adb_prefix}" "swipe_right"
    grab_screenshot "${adb_prefix}" "10-Dashboard.png" "Change the day or keep today"
 
    # Calendar
    perform_input "${adb_prefix}" "touch_element" "Calendar"
    grab_screenshot "${adb_prefix}" "30-Calendar.png" "Change the month of keep as-is"
    perform_input "${adb_prefix}" "go_back"
 
    # Devices
    perform_input "${adb_prefix}" "swipe_left"
    grab_screenshot "${adb_prefix}" "11-Devices.png"
 
    # Activity chart
    perform_input "${adb_prefix}" "touch_element" "Your activity"
    grab_screenshot "${adb_prefix}" "20-Activity.png" "Adjust the chart"
 
    # Sleep chart (Day)
    perform_input "${adb_prefix}" "touch_element" "Sleep"
    perform_input "${adb_prefix}" "touch_element" "Day"
    grab_screenshot "${adb_prefix}" "40-SleepPerDay.png" "Change the day or keep today"
 
    # Sleep chart (Week)
    perform_input "${adb_prefix}" "touch_element" "Week"
    grab_screenshot "${adb_prefix}" "41-SleepPerWeek.png" "Change the day or keep today"
 
    # Steps chart (Day)
    perform_input "${adb_prefix}" "touch_element" "Steps"
    perform_input "${adb_prefix}" "touch_element" "Day"
    grab_screenshot "${adb_prefix}" "50-StepsPerDay.png" "Change the day or keep today"
 
    # Steps chart (Week)
    perform_input "${adb_prefix}" "touch_element" "Week"
    grab_screenshot "${adb_prefix}" "51-StepsPerWeek.png" "Change the day or keep today"
 
    # Steps chart (Month)
    perform_input "${adb_prefix}" "touch_element" "Month"
    grab_screenshot "${adb_prefix}" "52-StepsPerMonth.png" "Change the day or keep today"
 
    # Stress
    perform_input "${adb_prefix}" "touch_element" "Stress"
    grab_screenshot "${adb_prefix}" "60-Stress.png" "Change the day or keep today"
 
    # Live activity
    perform_input "${adb_prefix}" "touch_element" "Live activity"
    grab_screenshot "${adb_prefix}" "70-LiveActivity.png" "Start your activity on the watch"
 
    # Back to the home screen
    perform_input "${adb_prefix}" "go_back"
 
    # Sport activities
    perform_input "${adb_prefix}" "touch_element" "Your activity tracks"
    grab_screenshot "${adb_prefix}" "70-SportActivities.png"
 
    # Sport activity filter
    perform_input "${adb_prefix}" "touch_element" "Sports Activities Filter"
    grab_screenshot "${adb_prefix}" "71-SportActivitiesFilter.png"
 
    # Back to the sport activities screen
    perform_input "${adb_prefix}" "go_back"
 
    # Sport activity summary
    perform_input "${adb_prefix}" "touch_element" "Device image" 10
    grab_screenshot "${adb_prefix}" "72-SportActivitiesSummary.png"
}
 
# Disable demo UI, and revert all applied settings by eval-ing all
# revert commands that is saved before.
revert() {
    if [ -n "${REVERT_COMMANDS}" ]; then
        echo; echo "[*] Reverting modifications..."
    fi
    eval "${REVERT_COMMANDS}"
    exit 0
}
 
start() {
    check_deps
 
    echo "[*] Searching for devices..."
    adb_command=$(get_adb_command)
    use_package=$(get_gadgetbridge_app "${adb_command}")
 
    echo; echo "[*] Applying preferences..."
    set_system_theme "${adb_command}"
    set_status_bar_lineage_os "${adb_command}"
    set_status_bar "${adb_command}"
 
    launch_gadgetbridge "${adb_command}" "${use_package}"
 
    echo; echo "[*] Taking screenshots..."
    take_screenshots "${adb_command}"
    optimize_screenshots
}
 
# Run "revert" function to revert everything if received an SIGINT or SIGTERM signal.
# Also with "EXIT" signal, run this function whenever the script exits.
trap revert SIGINT SIGTERM EXIT
 
start