#!/bin/bash getlatestapk() { # This functions finds the latest available version for the given architeture. aaptcmd="$3" sourceapk="" highversion="" tempapks=( ) OLDIFS="$IFS" IFS=" " # We set IFS to newline here so that filenames containing spaces are # handled correctly in the for-loops below. local LZ_DECOMPRESS_CMD="false" local LZ_DECOMPRESS_ARG="" if command -v lunzip > /dev/null; then LZ_DECOMPRESS_CMD="lunzip" elif command -v lzip > /dev/null; then LZ_DECOMPRESS_CMD="lzip" LZ_DECOMPRESS_ARG="--decompress" fi; # decompress apks # some apks are lz compressed to work around github file-size limits. for foundapklz in $(find "$1" -iname '*.apk.lz'); do $LZ_DECOMPRESS_CMD $LZ_DECOMPRESS_ARG --keep "$foundapklz" || echo "Warning: lz decompress command failed for $foundapklz" >&2 done # sed copies filename to the beginning, to compare version, and later we remove it with cut for foundapk in $(find "$1" -iname '*.apk' | sed 's!.*/\(.*\)!\1/&!' | sort -r -t/ -k1,1 -n | cut -d/ -f2-); do # Get package version name apkprops="$("$aaptcmd" dump badging "$foundapk" 2> /dev/null)" apkversion="$(echo "$apkprops" | awk -F="'" '/versionName=/ {print $4}' | sed "s/'.*//g")" # Get invariant version name. # We first remove evrything after the first space. apkversion="$(printf "$apkversion" | cut -d ' ' -f 1)" # Then, we remove everything after the fourth dot. apkversionparts=$(echo "$apkversion" | tr '.' '\n') j=0 apkversion="" for part in ${apkversionparts[@]}; do if [ $j -ge 4 ]; then break; else if [ $j -ge 1 ]; then apkversion+="." fi apkversion+="$part" ((j++)) fi done if [ -z "$highversion" ]; then # Take note of the highest version. highversion="$apkversion" elif [ "$highversion" != "$apkversion" ]; then # Skip lower versions. break; fi # Add package to list. tempapks+=("$foundapk") done if [ ${#tempapks[@]} -ge 2 ]; then # See if there is a package for this dpi or a nodpi package. for foundapk in ${tempapks[@]}; do dpipath="$(dirname "$foundapk")" dpi="$(basename "$dpipath")" if [[ "$dpi" = "nodpi" || "$dpi" = "$2" ]]; then sourceapk="$foundapk" break; fi if [ "$2" != "nodpi" ]; then # Check if the package dpi is a range and if the device falls between that range lodpi=${dpi%%-*} hidpi=${dpi##*-} if [[ "$lodpi" != "$dpi" && "$lodpi" -le "$2" && "$hidpi" -ge "$2" ]]; then sourceapk="$foundapk" break; fi fi done # If the device is set to nodpi, we take the highest dpi available. This should be the first one on the list. if [[ -z "$sourceapk" && "$2" = "nodpi" ]]; then sourceapk="$tempapks" fi # Take the lowest dpi above the device dpi or the highest dpi under the device dpi. if [ -z "$sourceapk" ]; then for foundapk in ${tempapks[@]}; do dpipath="$(dirname "$foundapk")" dpi="$(basename "$dpipath")" # If this is a range, we already know the device is either over or under that range. # Therefore, we can safely take any value. dpi=${dpi%%-*} if [ "$dpi" -ge "$2" ]; then sourceapk="$foundapk" else # If we havn't found a higher dpi, we take the first (highest) lower dpi. if [ -z "$sourceapk" ]; then sourceapk="$foundapk" fi break; fi done fi elif [ ${#tempapks[@]} -ge 1 ]; then # If theres only one apk, take that one. sourceapk="$tempapks" fi IFS="$OLDIFS" if [ -z "$sourceapk" ]; then echo "WARNING: No APK found compatible with API level $API for package $PACKAGE_NAME on $ARCH" >&2 return 1 #error fi #$sourceapk has the useful return value return 0 #return that it was a success } getconformapk() { # This functions finds the latest available version for the given architeture and dpi. IF the chosen dpi is not available, nodpi will be selected. sourceapk="" OLDIFS="$IFS" IFS=" " # We set IFS to newline here so that spaces can survive the for loop # sed copies filename to the beginning, to compare version, and later we remove it with cut for foundapk in $(find "$1" -iname '*.apk' | sed 's!.*/\(.*\)!\1/&!' | sort -r -t/ -k1,1 -n | cut -d/ -f2-); do founddpipath="$(dirname "$foundapk")" dpi="$(basename "$founddpipath")" lodpi=${dpi%%-*} hidpi=${dpi##*-} if [[ "$dpi" = "nodpi" || "$dpi" = "$2" || ( "$2" != "nodpi" && "$lodpi" != "$dpi" && "$lodpi" -le "$2" && "$hidpi" -ge "$2" ) ]]; then sourceapk="$foundapk" break; fi done IFS="$OLDIFS" if [ -z "$sourceapk" ]; then echo "WARNING: No APK found compatible with API level $API for package $PACKAGE_NAME on $ARCH and dpi $2" >&2 return 1 #error fi #$sourceapk has the useful return value return 0 #return that it was a success } getapk() { if [ "$3" = false ]; then aaptcmd="$4" if hash "$aaptcmd" 2> /dev/null; then getlatestapk "$1" "$2" "$aaptcmd" else echo "ERROR: aapt not found. Unable to detect latest package." >&2 return 1 fi else getconformapk "$1" "$2" fi return $? } getappapidir() { # Check if app directory exists. if [ ! -d "$1/$2/"*"app/$3" ]; then return 1 # Package does not exist for this arch. fi # Try to find the largest API which is greater or # equal to what is needed. api=0 for apidir in "$1/$2/"*"app/$3/"*; do currentApi="$(basename "$apidir")" if [ "$currentApi" -le "$4" ] && [ "$currentApi" -ge "$api" ]; then api="$currentApi" fi done # Have we found anything? if [ "$api" -gt 0 ]; then appapidir="$(echo "$1/$2/"*"app/$3/$api")" return 0 fi echo "WARNING: No APK found compatible with API level $4 for package $3 on $2" >&2 return 1 } getdpivalue() { case "$1" in "xxxhdpi") dpivalue="640"; ;; "xxhdpi") dpivalue="480"; ;; "xhdpi") dpivalue="320"; ;; "hdpi") dpivalue="240"; ;; "mdpi") dpivalue="160"; ;; "ldpi") dpivalue="120"; ;; *) return 1; ;; esac return 0; } SOURCES="$1" API="$2" DPI="$3" PACKAGE_NAME="$4" ARCH="$5" FORCE_DPI="$6" AAPT_CMD="$7" if ! getappapidir "$SOURCES" "$ARCH" "$PACKAGE_NAME" "$API"; then exit 1 fi if getdpivalue "$DPI"; then if getapk "$appapidir" "$dpivalue" "$FORCE_DPI" "$AAPT_CMD"; then echo "$sourceapk" else exit 1 fi elif getapk "$appapidir" "nodpi" "$FORCE_DPI" "$AAPT_CMD"; then echo "$sourceapk" else exit 1 fi