Last active
September 1, 2025 18:10
-
-
Save emilwojcik93/3ca0b063055d37a2fb1af10ab2a4aa2b to your computer and use it in GitHub Desktop.
This Termux script automatically detects and sets the optimal resolution for virtual displays based on connected external displays. It handles the entire process including dependency installation, ADB pairing/connection, external display detection, and resolution configuration.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/data/data/com.termux/files/usr/bin/env bash | |
# filepath: /data/data/com.termux/files/home/termux_display_resolution.sh | |
# Set Termux-specific paths | |
TERMUX_PREFIX="/data/data/com.termux/files/usr" | |
TERMUX_HOME="/data/data/com.termux/files/home" | |
TEMP_DIR="$TERMUX_HOME/.temp" | |
# Create temp directory if it doesn't exist | |
mkdir -p "$TEMP_DIR" | |
# Function to show colored output | |
print_colored() { | |
local color="$1" | |
local message="$2" | |
case "$color" in | |
"red") echo -e "\033[1;31m$message\033[0m" ;; | |
"green") echo -e "\033[1;32m$message\033[0m" ;; | |
"yellow") echo -e "\033[1;33m$message\033[0m" ;; | |
"blue") echo -e "\033[1;34m$message\033[0m" ;; | |
*) echo -e "$message" ;; | |
esac | |
} | |
# Function to print header | |
print_header() { | |
print_colored "blue" "=====================================" | |
print_colored "blue" " Display Resolution Optimizer Tool " | |
print_colored "blue" "=====================================" | |
echo "" | |
} | |
# Function to print usage | |
print_usage() { | |
echo "Usage: $0" | |
echo "This script sets virtual display resolution to match external displays." | |
echo "Works from Termux environment and handles dependencies installation." | |
exit 1 | |
} | |
# Function to install dependencies | |
install_dependencies() { | |
print_colored "yellow" "Checking for required packages..." | |
# Ensure package lists are up to date | |
pkg update -y | |
# Check for and install required packages | |
for package in android-tools grep coreutils sed; do | |
if ! dpkg -s "$package" >/dev/null 2>&1; then | |
print_colored "yellow" "Installing $package..." | |
pkg install -y "$package" | |
if [ $? -ne 0 ]; then | |
print_colored "red" "Failed to install $package. Exiting." | |
exit 1 | |
fi | |
fi | |
done | |
print_colored "green" "All dependencies are installed." | |
} | |
# Function to handle ADB pairing | |
handle_adb_pairing() { | |
# Check if any devices are already connected | |
if adb devices | grep -q "device$"; then | |
print_colored "green" "ADB device already connected." | |
return 0 | |
fi | |
# Function to get the local IP address | |
get_local_ip() { | |
# Try using ifconfig first for wlan0 (WiFi) | |
ifconfig wlan0 2>/dev/null | grep -oP 'inet addr:\K\d+(\.\d+){3}' || \ | |
ifconfig wlan0 2>/dev/null | grep -oP 'inet\s+\K\d+(\.\d+){3}' || \ | |
# Then try any interface except lo (loopback) | |
ifconfig 2>/dev/null | grep -v '127.0.0.1' | grep -oP 'inet addr:\K\d+(\.\d+){3}' || \ | |
ifconfig 2>/dev/null | grep -v '127.0.0.1' | grep -oP 'inet\s+\K\d+(\.\d+){3}' | |
} | |
local_ip=$(get_local_ip) | |
# Check if local_ip is in valid IP format | |
if [[ -n "$local_ip" && $local_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
print_colored "blue" "Your device IP address appears to be: $local_ip" | |
# Ask for target device IP with the local IP as default | |
print_colored "blue" "Enter target device IP address [$local_ip]:" | |
read device_ip | |
# If user pressed enter without typing, use the local_ip as default | |
if [ -z "$device_ip" ]; then | |
device_ip="$local_ip" | |
print_colored "yellow" "Using local IP address: $device_ip" | |
fi | |
else | |
# If no valid local IP was found | |
print_colored "yellow" "Could not determine your local IP address." | |
print_colored "blue" "Enter target device IP address:" | |
read device_ip | |
# Ensure the user entered something | |
while [ -z "$device_ip" ]; do | |
print_colored "red" "IP address cannot be empty." | |
print_colored "blue" "Enter target device IP address:" | |
read device_ip | |
done | |
fi | |
# Default ports | |
connect_port=5555 | |
pair_port=5555 # Most devices use 5555 for pairing too but internally subtract 1 | |
print_colored "blue" "Enter ADB connection port [5555]:" | |
read port_input | |
if [ -n "$port_input" ]; then | |
connect_port=$port_input | |
fi | |
# Try to connect first | |
print_colored "yellow" "Trying to connect to $device_ip:$connect_port..." | |
adb connect "$device_ip:$connect_port" | |
# Check if connection was successful | |
if adb devices | grep -q "$device_ip:$connect_port.*device$"; then | |
print_colored "green" "Successfully connected to device." | |
return 0 | |
fi | |
# If connection failed, try pairing | |
print_colored "yellow" "Connection failed. Need to pair first." | |
# Ask for pairing port (often different from connect port) | |
print_colored "blue" "Enter ADB pairing port [Typically $((connect_port-1))]:" | |
read pair_port_input | |
if [ -n "$pair_port_input" ]; then | |
pair_port=$pair_port_input | |
else | |
pair_port=$((connect_port-1)) | |
fi | |
# Ask for pairing code | |
print_colored "blue" "Enter pairing code (shown on device):" | |
read pairing_code | |
# Pairing process | |
print_colored "yellow" "Pairing with device at $device_ip:$pair_port using code: $pairing_code" | |
adb pair "$device_ip:$pair_port" "$pairing_code" | |
# Try connecting again after pairing | |
print_colored "yellow" "Trying to connect after pairing..." | |
adb connect "$device_ip:$connect_port" | |
# Final check | |
if adb devices | grep -q "$device_ip:$connect_port.*device$"; then | |
print_colored "green" "Successfully connected to device after pairing." | |
return 0 | |
else | |
print_colored "red" "Failed to connect to ADB device after pairing attempts." | |
return 1 | |
fi | |
} | |
# Function to check for external display | |
check_external_display() { | |
print_colored "yellow" "Checking for external displays..." | |
# Get display information | |
adb shell dumpsys display > "$TEMP_DIR/display_info.txt" | |
# Check for HDMI or external display | |
if grep -q "HDMI\|EXTERNAL\|local:[^0]\|uniqueId=\"local:" "$TEMP_DIR/display_info.txt"; then | |
print_colored "green" "External display detected." | |
return 0 | |
else | |
print_colored "red" "No external display detected. Please connect an external display first." | |
return 1 | |
fi | |
} | |
# Function to find max resolution for external displays | |
find_max_resolution() { | |
print_colored "yellow" "Finding maximum resolution for external displays..." | |
# First try direct HDMI search | |
display_name="HDMI" | |
hdmi_max_res=$(adb shell dumpsys display | grep -m 1 "${display_name}" | awk -F' |=' '/width/ && /height/ {for(i=1;i<=NF;i++) if($i=="width") print $(i+1) "x" $(i+3)}' | tr -d ',' | sort -nr -k1,1 -k2,2 | head -n 1) | |
if [[ -n "$hdmi_max_res" ]]; then | |
print_colored "green" "Found max resolution from HDMI display: $hdmi_max_res" | |
max_resolution=$hdmi_max_res | |
return 0 | |
fi | |
# If HDMI search fails, try more extensive parsing | |
print_colored "yellow" "Checking all external display modes..." | |
max_width=0 | |
max_height=0 | |
max_resolution="" | |
# Get all external display modes | |
while IFS= read -r line; do | |
if [[ $line =~ DisplayDeviceInfo && ! $line =~ virtual && ! $line =~ displayId=0 ]]; then | |
# Check for supported modes | |
if [[ $line =~ supportedModes\ \[\{([^]]+)\}\] ]]; then | |
modes_info="${BASH_REMATCH[1]}" | |
# Process each mode | |
while IFS= read -r mode; do | |
if [[ $mode =~ width=([0-9]+),\ height=([0-9]+) ]]; then | |
width="${BASH_REMATCH[1]}" | |
height="${BASH_REMATCH[2]}" | |
# Calculate area | |
current_area=$((width * height)) | |
max_area=$((max_width * max_height)) | |
if [[ $max_width -eq 0 ]] || (( current_area > max_area )); then | |
max_width=$width | |
max_height=$height | |
max_resolution="${width}x${height}" | |
print_colored "yellow" "Found resolution: ${width}x${height} (new max)" | |
fi | |
fi | |
done < <(echo "$modes_info" | tr '{' '\n') | |
fi | |
fi | |
done < "$TEMP_DIR/display_info.txt" | |
# If we found a resolution | |
if [[ -n "$max_resolution" ]]; then | |
print_colored "green" "Found max resolution from modes: $max_resolution" | |
return 0 | |
fi | |
# Last attempt - check basic width/height attributes | |
while IFS= read -r line; do | |
if [[ $line =~ "DisplayDeviceInfo" && ! $line =~ "virtual" && ! $line =~ "displayId=0" ]]; then | |
if [[ $line =~ width=([0-9]+) && $line =~ height=([0-9]+) ]]; then | |
width=$(echo "$line" | grep -oP "width=\K[0-9]+") | |
height=$(echo "$line" | grep -oP "height=\K[0-9]+") | |
current_area=$((width * height)) | |
max_area=$((max_width * max_height)) | |
if (( current_area > max_area )); then | |
max_width=$width | |
max_height=$height | |
max_resolution="${width}x${height}" | |
fi | |
fi | |
fi | |
done < "$TEMP_DIR/display_info.txt" | |
if [[ -n "$max_resolution" ]]; then | |
print_colored "green" "Found max resolution: $max_resolution" | |
return 0 | |
else | |
print_colored "red" "Could not determine external display resolution." | |
return 1 | |
fi | |
} | |
# Function to find virtual display | |
find_virtual_display() { | |
print_colored "yellow" "Looking for virtual displays..." >&2 | |
# Multiple strategies to find virtual display ID | |
virtual_display_id="" | |
# First strategy: search for uniqueId with virtual | |
grep -E "uniqueId=.*virtual" "$TEMP_DIR/display_info.txt" > "$TEMP_DIR/virtual_displays.txt" | |
while IFS= read -r line; do | |
if [[ $line =~ displayId[=\ ]+([0-9]+) ]]; then | |
virtual_display_id="${BASH_REMATCH[1]}" | |
print_colored "green" "Found virtual display ID: $virtual_display_id" >&2 | |
break | |
fi | |
done < "$TEMP_DIR/virtual_displays.txt" | |
# Second strategy: search for mUniqueDisplayId=virtual | |
if [[ -z "$virtual_display_id" ]]; then | |
while IFS= read -r line; do | |
if [[ $line == *"mUniqueDisplayId=virtual"* ]]; then | |
display_section=$(grep -A 5 "$line" "$TEMP_DIR/display_info.txt") | |
if [[ $display_section =~ displayId[\ =]+([0-9]+) ]]; then | |
virtual_display_id="${BASH_REMATCH[1]}" | |
print_colored "green" "Found virtual display ID from mUniqueDisplayId: $virtual_display_id" >&2 | |
break | |
fi | |
fi | |
done < "$TEMP_DIR/display_info.txt" | |
fi | |
# Third strategy: look for "virtual" and "displayId" together | |
if [[ -z "$virtual_display_id" ]]; then | |
display_line=$(grep -m 1 -E 'virtual.*displayId|displayId.*virtual' "$TEMP_DIR/display_info.txt") | |
if [[ $display_line =~ displayId[\ =]+([0-9]+) ]]; then | |
virtual_display_id="${BASH_REMATCH[1]}" | |
print_colored "green" "Found virtual display ID via direct search: $virtual_display_id" >&2 | |
fi | |
fi | |
# Fourth strategy: Look for "Desktop" display which is usually virtual | |
if [[ -z "$virtual_display_id" ]]; then | |
desktop_line=$(grep -m 1 -B 3 -A 3 '"Desktop"' "$TEMP_DIR/display_info.txt") | |
if [[ $desktop_line =~ displayId[\ =]+([0-9]+) ]]; then | |
virtual_display_id="${BASH_REMATCH[1]}" | |
print_colored "green" "Found Desktop virtual display ID: $virtual_display_id" >&2 | |
fi | |
fi | |
# Fifth strategy: try ID=2 as fallback (common for virtual displays) | |
if [[ -z "$virtual_display_id" ]]; then | |
virtual_display_id="2" | |
print_colored "yellow" "Using fallback virtual display ID: $virtual_display_id" >&2 | |
fi | |
# Only output the raw ID number without any extra text or formatting | |
echo "$virtual_display_id" | |
} | |
# Function to set resolution for virtual display | |
set_virtual_resolution() { | |
local display_id="$1" | |
local resolution="$2" | |
width=${resolution%x*} | |
height=${resolution#*x} | |
print_colored "yellow" "Setting virtual display (ID: $display_id) to resolution: $resolution..." | |
# Set the resolution | |
adb shell wm size $width\x$height -d $display_id | |
# Check result | |
if [ $? -eq 0 ]; then | |
# Verify the change | |
current_res=$(adb shell wm size -d $display_id) | |
print_colored "green" "Resolution set successfully!" | |
print_colored "blue" "Current configuration:\n$current_res" | |
return 0 | |
else | |
print_colored "red" "Failed to set resolution." | |
return 1 | |
fi | |
} | |
# Function to clean up unreachable ADB connections | |
clean_unreachable_adb_connections() { | |
print_colored "yellow" "Checking existing ADB connections..." | |
# Get list of connected devices, excluding the header and empty lines | |
adb_devices=$(adb devices | grep -v "List" | sed '/^$/d') | |
if [ -z "$adb_devices" ]; then | |
# No devices connected, nothing to clean | |
print_colored "yellow" "No ADB devices currently connected." | |
return 0 | |
fi | |
print_colored "yellow" "Found connected devices, checking their status..." | |
has_unreachable=false | |
# Check each device | |
while IFS= read -r line; do | |
device=$(echo "$line" | awk '{print $1}') | |
status=$(echo "$line" | awk '{print $2}') | |
# Only process network devices (with IP:port format) | |
if [[ "$device" == *":"* ]]; then | |
print_colored "blue" "Testing connection to $device..." | |
# Try a simple command with a short timeout | |
if ! timeout 2 adb -s "$device" shell echo "test" &>/dev/null; then | |
print_colored "yellow" "Device $device is unreachable, disconnecting..." | |
adb disconnect "$device" | |
has_unreachable=true | |
else | |
print_colored "green" "Device $device is responsive." | |
fi | |
fi | |
done <<< "$adb_devices" | |
if [ "$has_unreachable" = true ]; then | |
print_colored "green" "Cleaned up unreachable connections." | |
else | |
print_colored "green" "All connections are healthy." | |
fi | |
return 0 | |
} | |
# Main function | |
main() { | |
print_header | |
# Check if adb is available | |
if ! command -v adb &> /dev/null; then | |
print_colored "red" "ADB not found even after installing dependencies." | |
# Install dependencies | |
install_dependencies | |
exit 1 | |
fi | |
# Clean up any stale ADB connections before proceeding | |
clean_unreachable_adb_connections | |
# Handle ADB pairing and connection | |
handle_adb_pairing | |
if [ $? -ne 0 ]; then | |
print_colored "red" "Failed to establish ADB connection. Exiting." | |
exit 1 | |
fi | |
# Check for external display | |
check_external_display | |
if [ $? -ne 0 ]; then | |
exit 1 | |
fi | |
# Find maximum resolution | |
find_max_resolution | |
if [ $? -ne 0 ] || [ -z "$max_resolution" ]; then | |
print_colored "red" "Failed to determine maximum resolution. Exiting." | |
exit 1 | |
fi | |
# Find virtual display | |
print_colored "yellow" "Finding virtual display..." | |
virtual_display_id=$(find_virtual_display) | |
print_colored "blue" "Using virtual display ID: $virtual_display_id" | |
if [ -z "$virtual_display_id" ]; then | |
print_colored "red" "Failed to identify virtual display. Exiting." | |
exit 1 | |
fi | |
# Set resolution | |
set_virtual_resolution "$virtual_display_id" "$max_resolution" | |
if [ $? -ne 0 ]; then | |
print_colored "red" "Failed to set virtual display resolution. Exiting." | |
exit 1 | |
fi | |
# Clean up | |
rm -f "$TEMP_DIR/display_info.txt" "$TEMP_DIR/virtual_displays.txt" | |
print_colored "green" "Operation completed successfully!" | |
exit 0 | |
} | |
# Run the main function | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Android External Display Resolution Optimizer
This script automatically detects external displays connected to your Android device and sets the virtual display resolution to the optimal supported resolution. It works from Termux and handles all necessary ADB connections, pairing, and configuration.
Features
Prerequisites
Install Termux and Termux:Widget from GitHub (F-Droid versions recommended):
Install required packages:
pkg update && pkg install android-tools wget curl
Installation
One-Step Installation (Widget Ready)
Run this command in Termux to download the script and create necessary directories:
Usage
Running Directly in Termux
~/.termux/widget/dynamic_shortcuts/termux_display_resolution.sh
Using the Widget Shortcuts (Two Methods)
Method 1: Via Termux:Widget on home screen
Method 2: Via Termux Context Menu
Example Output
Troubleshooting
If the script fails to connect via ADB, make sure:
If resolution doesn't change, try manually entering your device IP and port when prompted
If shortcuts don't appear, check directory permissions with:
Requirements