diff --git a/src/mainline/fastlane/collect_screenshots.sh b/src/mainline/fastlane/collect_screenshots.sh deleted file mode 100755 index 312dd4720c..0000000000 --- a/src/mainline/fastlane/collect_screenshots.sh +++ /dev/null @@ -1,117 +0,0 @@ -#!/bin/bash -#This script needs bash for the read command -#Takes screenshots of Gadgetbridge for the fastlane. -#Live Activity screenshot not taken, as it needs more inputs. - -VERSION="0407" -#Version sets clock in Android demo mode to version -#to indicate when screenshots have been taken - -DIR="metadata/android/en-US/images/phoneScreenshots/" - -#enable demo mode -adb shell settings put global sysui_demo_allowed 1 -adb shell am broadcast -a com.android.systemui.demo -e command enter -adb shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm $VERSION -adb shell am broadcast -a com.android.systemui.demo -e command notifications -e visible false -adb shell am broadcast -a com.android.systemui.demo -e command battery -e level 100 -adb shell am broadcast -a com.android.systemui.demo -e command network -e wifi show -e level 4 -adb shell am broadcast -a com.android.systemui.demo -e command network -e mobile show -e datatype none -e level 4 - -#launch Gadgetbridge -adb shell monkey -p nodomain.freeyourgadget.gadgetbridge -c android.intent.category.LAUNCHER 1 -sleep 3 - -#Start taking screenshots: - -adb shell screencap -p > $DIR"10-MainScreen.png" - - -adb shell input tap 455 355 -read -p "Select a day and press Enter" -adb shell screencap -p > $DIR"20-ActivityAndSleep.png" - -adb shell input tap 240 210 -read -p "slightly adjust label on chart and press Enter" -adb shell screencap -p > $DIR"30-Sleep.png" - -adb shell input tap 420 210 -read -p "slightly adjust label on chart and press Enter" -adb shell screencap -p > $DIR"40-SleepPerWeek.png" - -#switch to month -adb shell input tap 670 100 -sleep 0.5 -adb shell input tap 670 1080 -sleep 0.5 -adb shell input keyevent 111 -read -p "slightly adjust label on chart and press Enter" - -adb shell screencap -p > $DIR"41-SleepPerMonth.png" - - -adb shell input tap 590 210 -sleep 0.5 -adb shell screencap -p > $DIR"51-StepsPerMonth.png" - -#switch back to week -adb shell input tap 670 100 -sleep 0.5 -adb shell input tap 670 1080 -sleep 0.5 -adb shell input keyevent 111 -read -p "Select a day and press Enter" - -adb shell screencap -p > $DIR"50-StepsPerWeek.png" - -adb shell input tap 590 210 -sleep 0.5 -adb shell screencap -p > $DIR"60-SpeedZones.png" - -#Go back to main screen -adb shell input keyevent 111 - -#sports activities -adb shell input tap 540 355 -read -p "Slide the activities and press Enter" -adb shell screencap -p > $DIR"70-SportActivities.png" - - -adb shell input tap 490 100 -read -p "Set the filter and press Enter" -adb shell screencap -p > $DIR"71-SportActivitiesFilter.png" -adb shell input tap 315 1100 -sleep 0.5 -adb shell input tap 585 110 -sleep 0.5 -adb shell screencap -p > $DIR"72-SportActivitiesSummary.png" - -#dark mode. Here, we re-use old pictures, which due to a bug in f.droid, keep being cached on the server. -#so we at least use them for dark mode, but do not add new ones - -read -p "Set dark mode and press Enter" - -adb shell screencap -p > $DIR"1-MainScreen.png" - -adb shell input tap 455 355 -read -p "Select a day and press Enter" -adb shell screencap -p > $DIR"2-ActivityAndSleep.png" - -adb shell input tap 420 210 -read -p "slightly adjust label on chart and press Enter" -adb shell screencap -p > $DIR"3-SleepPerWeek.png" - -adb shell screencap -p > $DIR"4-StepsPerWeek.png" - -adb shell input tap 590 210 -sleep 0.5 -adb shell input tap 590 210 -sleep 0.5 -adb shell screencap -p > $DIR"5-SpeedZones.png" - -#Go back to main screen -adb shell input keyevent 111 - -#disable demo mode -adb shell am broadcast -a com.android.systemui.demo -e command exit -adb shell settings put global sysui_demo_allowed 0 diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/1-MainScreen.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/1-MainScreen.png deleted file mode 100644 index d7b971af91..0000000000 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/1-MainScreen.png and /dev/null differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/10-Dashboard.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/10-Dashboard.png new file mode 100644 index 0000000000..c2bb7621cb Binary files /dev/null and b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/10-Dashboard.png differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/10-MainScreen.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/10-MainScreen.png deleted file mode 100644 index 5a43d3355b..0000000000 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/10-MainScreen.png and /dev/null differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/11-Devices.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/11-Devices.png new file mode 100644 index 0000000000..c6448d7ba1 Binary files /dev/null and b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/11-Devices.png differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/2-ActivityAndSleep.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/2-ActivityAndSleep.png deleted file mode 100644 index 3f5fa9e61b..0000000000 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/2-ActivityAndSleep.png and /dev/null differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/20-Activity.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/20-Activity.png new file mode 100644 index 0000000000..f5c154ebab Binary files /dev/null and b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/20-Activity.png differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/20-ActivityAndSleep.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/20-ActivityAndSleep.png deleted file mode 100644 index 597d5a108c..0000000000 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/20-ActivityAndSleep.png and /dev/null differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/3-SleepPerWeek.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/3-SleepPerWeek.png deleted file mode 100644 index 823e8d4bb3..0000000000 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/3-SleepPerWeek.png and /dev/null differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/30-Sleep.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/30-Sleep.png deleted file mode 100644 index 857db969d3..0000000000 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/30-Sleep.png and /dev/null differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/4-StepsPerWeek.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/4-StepsPerWeek.png deleted file mode 100644 index 823e8d4bb3..0000000000 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/4-StepsPerWeek.png and /dev/null differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/40-SleepPerDay.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/40-SleepPerDay.png new file mode 100644 index 0000000000..ba8062329a Binary files /dev/null and b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/40-SleepPerDay.png differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/40-SleepPerWeek.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/40-SleepPerWeek.png deleted file mode 100644 index be5181b365..0000000000 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/40-SleepPerWeek.png and /dev/null differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/41-SleepPerMonth.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/41-SleepPerMonth.png deleted file mode 100644 index 1d7e271b91..0000000000 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/41-SleepPerMonth.png and /dev/null differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/41-SleepPerWeek.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/41-SleepPerWeek.png new file mode 100644 index 0000000000..f0feb3624c Binary files /dev/null and b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/41-SleepPerWeek.png differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/5-SpeedZones.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/5-SpeedZones.png deleted file mode 100644 index 72fe0ece9c..0000000000 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/5-SpeedZones.png and /dev/null differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/50-StepsPerDay.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/50-StepsPerDay.png new file mode 100644 index 0000000000..dcebda530f Binary files /dev/null and b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/50-StepsPerDay.png differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/50-StepsPerWeek.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/50-StepsPerWeek.png deleted file mode 100644 index 26d287b99e..0000000000 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/50-StepsPerWeek.png and /dev/null differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/51-StepsPerMonth.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/51-StepsPerMonth.png deleted file mode 100644 index fdec87b6f8..0000000000 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/51-StepsPerMonth.png and /dev/null differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/51-StepsPerWeek.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/51-StepsPerWeek.png new file mode 100644 index 0000000000..7e3f04c225 Binary files /dev/null and b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/51-StepsPerWeek.png differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/52-StepsPerMonth.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/52-StepsPerMonth.png new file mode 100644 index 0000000000..eb8cd1e519 Binary files /dev/null and b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/52-StepsPerMonth.png differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/60-SpeedZones.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/60-SpeedZones.png deleted file mode 100644 index a70ac10cd6..0000000000 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/60-SpeedZones.png and /dev/null differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/60-Stress.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/60-Stress.png new file mode 100644 index 0000000000..c6890c8021 Binary files /dev/null and b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/60-Stress.png differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/70-LiveActivity.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/70-LiveActivity.png index 1eaedecbfc..99468735ae 100644 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/70-LiveActivity.png and b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/70-LiveActivity.png differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/70-SportActivities.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/70-SportActivities.png index 983d1fc183..e3b69609e8 100644 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/70-SportActivities.png and b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/70-SportActivities.png differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/71-SportActivitiesFilter.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/71-SportActivitiesFilter.png index 9e3b7f51c7..58c82a775d 100644 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/71-SportActivitiesFilter.png and b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/71-SportActivitiesFilter.png differ diff --git a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/72-SportActivitiesSummary.png b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/72-SportActivitiesSummary.png index e775fc5039..cd158b7ebe 100644 Binary files a/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/72-SportActivitiesSummary.png and b/src/mainline/fastlane/metadata/android/en-US/images/phoneScreenshots/72-SportActivitiesSummary.png differ diff --git a/src/mainline/fastlane/take_app_screenshots.sh b/src/mainline/fastlane/take_app_screenshots.sh new file mode 100755 index 0000000000..fc1c901351 --- /dev/null +++ b/src/mainline/fastlane/take_app_screenshots.sh @@ -0,0 +1,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 . +# + +# 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 \ No newline at end of file