├── .github ├── FUNDING.yml └── workflows │ └── main.yml ├── .gitignore ├── README.md ├── files.txt ├── module ├── META-INF │ └── com │ │ └── google │ │ └── android │ │ ├── update-binary │ │ └── updater-script ├── QuickSwitch.apk ├── common │ ├── aapt2_arm64-v8a │ ├── aapt2_armeabi-v7a │ ├── aapt2_x86 │ └── aapt2_x86_64 ├── customize.sh ├── overlays │ ├── AndroidManifest.xml │ └── overlay │ │ └── values │ │ └── bools.xml ├── quickswitch ├── sepolicy.rule ├── service.sh ├── system.prop ├── system │ └── placeholder ├── uninstall.sh ├── zipsigner └── zipsigner-3.0-dexed.jar ├── release.sh ├── webui ├── CHANGELOG.md ├── module.prop ├── update.json └── webroot │ ├── index.html │ ├── modules │ ├── dashboard.js │ ├── exec.js │ ├── kernelsu.js │ ├── launchers.js │ ├── loader.js │ ├── navbar.js │ └── process.js │ ├── scripts │ └── genQuickstepList.sh │ └── style.css └── withoutui ├── CHANGELOG.md ├── module.prop └── update.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [Skittles9823] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['paypal.me/Skittles2398', 'paypal.me/Paphonb'] 13 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v4 12 | 13 | - name: Rebuild apk 14 | if: endsWith(github.ref, '/master') 15 | env: 16 | DRONE_SERVER: ${{ secrets.DRONE_SERVER }} 17 | DRONE_TOKEN: ${{ secrets.DRONE_TOKEN }} 18 | DRONE_REPO: ${{ secrets.DRONE_REPO }} 19 | run: | 20 | bash ./restartbuild.sh 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QuickSwitch fork 2 | 3 | This is a fork of QuickSwitch. 4 | QuickSwitch is a Magisk/KernelSU/Apatch module which systemlessly allows supported launchers to access the recents (QuickStep) APIs. 5 | 6 | To learn about the original, see [here](https://github.com/skittles9823/QuickSwitch). 7 | Also, please do not report bugs in forked versions upstream. 8 | 9 | ## Requirements: 10 | 11 | - Latest version of Magisk or KernelSU or Apatch 12 | - Android 9+ 13 | 14 | ## ⚠Warning⚠ 15 | This module operates the system using Root privileges. 16 | Please use it with extreme caution. 17 | Also, many users in the Telegram community have encountered trouble without reading the Readme. 18 | Please read the Readme carefully before using this module. 19 | 20 | This is not limited to this module, 21 | When you operate the system, you need to understand how to use it and manage the risk. 22 | 23 | ## Installation: 24 | 25 | ## Magisk/KSU/APatch: 26 | 1. Install the latest QuickSwitch zip from the [GitHub releases](https://github.com/j7b3y/QuickSwitch/releases/latest). 27 | 2. `su -c /data/adb/modules/quickswitch/quickswitch --ch=launcher.package.name` 28 | 3. Reboot. 29 | 4. Verify your new recents provider is correct. 30 | 5. Set the new recents provider as the default launcher. 31 | 6. Profit. 32 | 33 | ※ Please run it in a terminal application (termux, etc.) with root access. 34 | ※ Replace launcher.package.name with the name of your launcher. For example app.lawnchair.debug. 35 | 36 | ## Web Interface 37 | This is not complete but may help users who are having trouble with command line operations. 38 | If you are a magisk user, you will need the following apps. 39 | - [KsuWebUIStandalone](https://github.com/5ec1cff/KsuWebUIStandalone) 40 | 41 | ## Support: 42 | If you encounter any problems, you may find some hints in the github issues or [Telegram Group](https://t.me/QuickstepSwitcherSupport). 43 | 44 | ## Branches 45 | - master 46 | Original source. 47 | - anyfix/* 48 | It was to be used for pull requests. 49 | - WIP/* 50 | With incomplete new elements. -------------------------------------------------------------------------------- /files.txt: -------------------------------------------------------------------------------- 1 | common* 2 | META-INF* 3 | overlays* 4 | system* 5 | customize.sh 6 | quickswitch 7 | QuickSwitch.apk 8 | sepolicy.rule 9 | service.sh 10 | system.prop 11 | uninstall.sh 12 | zipsigner 13 | zipsigner-3.0-dexed.jar -------------------------------------------------------------------------------- /module/META-INF/com/google/android/update-binary: -------------------------------------------------------------------------------- 1 | #!/sbin/sh 2 | 3 | ################# 4 | # Initialization 5 | ################# 6 | 7 | umask 022 8 | 9 | # echo before loading util_functions 10 | ui_print() { echo "$1"; } 11 | 12 | require_new_magisk() { 13 | ui_print "*******************************" 14 | ui_print " Please install Magisk v20.4+! " 15 | ui_print "*******************************" 16 | exit 1 17 | } 18 | 19 | ######################### 20 | # Load util_functions.sh 21 | ######################### 22 | 23 | OUTFD=$2 24 | ZIPFILE=$3 25 | 26 | mount /data 2>/dev/null 27 | 28 | [ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk 29 | . /data/adb/magisk/util_functions.sh 30 | [ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk 31 | 32 | install_module 33 | exit 0 34 | -------------------------------------------------------------------------------- /module/META-INF/com/google/android/updater-script: -------------------------------------------------------------------------------- 1 | #MAGISK 2 | -------------------------------------------------------------------------------- /module/QuickSwitch.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j7b3y/QuickSwitch/7f08bdbca7075c5ae29848c885808c5f82db402f/module/QuickSwitch.apk -------------------------------------------------------------------------------- /module/common/aapt2_arm64-v8a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j7b3y/QuickSwitch/7f08bdbca7075c5ae29848c885808c5f82db402f/module/common/aapt2_arm64-v8a -------------------------------------------------------------------------------- /module/common/aapt2_armeabi-v7a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j7b3y/QuickSwitch/7f08bdbca7075c5ae29848c885808c5f82db402f/module/common/aapt2_armeabi-v7a -------------------------------------------------------------------------------- /module/common/aapt2_x86: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j7b3y/QuickSwitch/7f08bdbca7075c5ae29848c885808c5f82db402f/module/common/aapt2_x86 -------------------------------------------------------------------------------- /module/common/aapt2_x86_64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j7b3y/QuickSwitch/7f08bdbca7075c5ae29848c885808c5f82db402f/module/common/aapt2_x86_64 -------------------------------------------------------------------------------- /module/customize.sh: -------------------------------------------------------------------------------- 1 | SKIPUNZIP=1 2 | # @Skittles9823 made this ascii and is way to proud of it 3 | ui_print " " 4 | ui_print " _____ " 5 | ui_print " __ | | __ " 6 | ui_print " | || || | " 7 | ui_print " | || || | " 8 | ui_print " |__|| ||__| " 9 | ui_print " |_____| " 10 | ui_print " QuickSwitch " 11 | ui_print " " 12 | ui_print " The Lawnchair Team" 13 | ui_print " " 14 | 15 | [ $BOOTMODE == "false" ] && abort "Installation failed! QuickSwitch must be installed via Magisk/KernelSU Manager!" 16 | [ $API -lt "28" ] && abort "QuickSwitch is for Android Pie+ only" 17 | 18 | VEN=/system/vendor 19 | [ -L /system/vendor ] && VEN=/vendor 20 | if [ -f $VEN/build.prop ]; then BUILDS="/system/build.prop $VEN/build.prop"; else BUILDS="/system/build.prop"; fi 21 | # Thanks Narsil/Sauron for the huge props list for various android systems 22 | # Far easier to look there then ask users for their build.props 23 | MIUI=$(grep "ro.miui.ui.version.*" $BUILDS) 24 | if [ $MIUI ] && [ $API -lt "30" ]; then 25 | ui_print " MIUI 12 or lower is not supported" 26 | abort " Aborting..." 27 | fi 28 | ui_print "- Extracting module files" 29 | 30 | unzip -o "$ZIPFILE" 'overlays/*' 'system/*' 'common/*' 'module.prop' 'system.prop' 'sepolicy.rule' 'zipsigner*' 'uninstall.sh' 'quickswitch' 'service.sh' 'webroot/*' -d $MODPATH >&2 31 | chmod +x $MODPATH/common/* 32 | 33 | AAPT2=aapt2_$(getprop ro.product.cpu.abi) 34 | cp -af $MODPATH/common/$AAPT2 $MODPATH/aapt2 || abort "Unsupported Arch!" 35 | rm -rf $MODPATH/common 36 | rm -rf /data/adb/service.d/quickswitch.sh 37 | rm -rf /data/adb/service.d/quickswitch-service.sh 38 | rm -rf /data/adb/post-fs-data.d/quickswitch-post.sh 39 | 40 | # Custom install stuffs 41 | rm -rf /data/resource-cache/overlays.list 42 | find /data/resource-cache/ -name "*QuickstepSwitcherOverlay*" -exec rm -rf {} \; 43 | find /data/resource-cache/ -name "*QuickSwitchOverlay*" -exec rm -rf {} \; 44 | MODULEDIR="/data/adb/modules/$MODID" 45 | MODVER=$(grep_prop versionCode $MODULEDIR/module.prop) 46 | 47 | # Check for root solution 48 | if [ -z "$KSU" ]; then 49 | sed -i "/KSU=true*/d" $MODPATH/quickswitch 50 | fi 51 | 52 | if [ -z "$APATCH" ]; then 53 | sed -i "/APATCH=true*/d" $MODPATH/quickswitch 54 | fi 55 | 56 | if [ -n "$KSU" ] || [ -n "$APATCH" ]; then 57 | NOAPK=true 58 | ln -s $(which busybox) $MODPATH/busybox 59 | if ( [ -n "$KSU" ] && [ -e "/data/adb/ksu/modules.img" ] ) || ( [ -n "$APATCH" ] && [ -z "$APATCH_BIND_MOUNT" ] ) ; then 60 | sed -i "/MAGIC_MOUNT=true*/d" $MODPATH/quickswitch 61 | fi 62 | else 63 | ln -s /data/adb/magisk/busybox $MODPATH/busybox 64 | fi 65 | 66 | 67 | if [ -z "$NOAPK" ]; then 68 | unzip -o "$ZIPFILE" 'QuickSwitch.apk' -d /data/local/tmp >&2 69 | ui_print "- installing QuickSwitch.apk" 70 | pm install -r "/data/local/tmp/QuickSwitch.apk" 71 | rm -rf /data/local/tmp/QuickSwitch.apk 72 | fi 73 | 74 | rm -rf /data/adb/modules/quickstepswitcher # yeet old module dir 75 | 76 | if [ -d $MODULEDIR ]; then 77 | if [ $MODVER -ge 3300 ]; then # Been a minute since we've made people clean the install dir, prolly should do it now 78 | ui_print "- Module updating - retaining current provider" 79 | for i in $(find $MODULEDIR/system/* -type d -maxdepth 0); do 80 | cp -rf "$i" $MODPATH/system/ 81 | done 82 | else 83 | for i in $(find $MODULEDIR/* -maxdepth 0 | sed "/^module.prop/ d"); do 84 | rm -rf "$i" 85 | done 86 | ui_print "- Major upgrade! clearing out all old files and directories." 87 | fi 88 | fi 89 | 90 | # Nobody reads it anyway Sadge 91 | 92 | set_perm_recursive $MODPATH 0 0 0755 0644 93 | set_perm $MODPATH/aapt2 2000 2000 0755 94 | set_perm $MODPATH/busybox 2000 2000 0755 95 | set_perm $MODPATH/quickswitch 2000 2000 0777 96 | set_perm $MODPATH/zipsigner 0 0 0755 97 | set_perm $MODPATH/zipsigner-3.0-dexed.jar 0 0 0644 98 | -------------------------------------------------------------------------------- /module/overlays/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /module/overlays/overlay/values/bools.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | true 5 | -------------------------------------------------------------------------------- /module/quickswitch: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | # Terminal Magisk Mod Template 3 | # by veez21 @ xda-developers 4 | # Slimmed down for use as an app backend by @Skittles9823 5 | 6 | # Module ID 7 | ID="quickswitch" 8 | 9 | # Mod Directory 10 | MODDIR=${0%/*} 11 | 12 | KSU=true # removed during install if false 13 | APATCH=true # removed during install if false 14 | MAGIC_MOUNT=true # removed during install if false 15 | 16 | if [ "$APATCH" ]; then 17 | APATCH_VER=$(cat /data/adb/ap/version) 18 | fi 19 | 20 | BBDIR="$MODDIR/busybox" 21 | 22 | cd $MODDIR 23 | 24 | # Set Log Files 25 | mkdir -p $MODDIR/logs 26 | # > Verbose output goes here 27 | VERLOG=$MODDIR/logs/$ID-verbose.log 28 | oldVERLOG=$MODDIR/logs/$ID-verbose-old.log 29 | 30 | # Start Logging verbosely 31 | mv -f $VERLOG $oldVERLOG 2>/dev/null 32 | mv -f $LOG $oldLOG 2>/dev/null 33 | set -x 2>$VERLOG 34 | 35 | ######## 36 | # Main # 37 | ######## 38 | API=$(getprop ro.build.version.sdk) 39 | PATH="$BBDIR:/sbin/:$PATH" 40 | alias aapt2='$MODDIR/aapt2' 41 | alias sign='$MODDIR/zipsigner' 42 | OVERLAYDIR=$MODDIR/overlays 43 | RAND=$(echo $RANDOM | sed -r 's|^(...).*|\1|') 44 | DID_MOUNT_RW= 45 | defaultlauncherPackage=$(aapt2 d strings /system/framework/framework-res.apk | grep -m 1 "com.android.quickstep.RecentsActivity" | awk '{ print $3 }' | sed "s|/.*||") 46 | defaultlauncherPath=$(grep -m1 -E "$defaultlauncherPackage.*codePath.*app" /data/system/packages.xml | sed -r "s/.*codePath=(.*)/\1/; s/\"//g" | awk '{ print $1 }') 47 | if [ ! $(echo $defaultlauncherPath | grep "/system/") ]; then 48 | defaultlauncherPath="/system${defaultlauncherPath}" 49 | fi 50 | 51 | abort() { 52 | echo "qsError: $1" 53 | } 54 | 55 | abortexit() { 56 | echo "qsError: $1" 57 | get_logs 58 | exit 1 59 | } 60 | 61 | # Functions to check if dirs is mounted 62 | is_mounted() { 63 | grep " $(readlink -f $1) " /proc/mounts 2>/dev/null 64 | return $? 65 | } 66 | 67 | is_mounted_rw() { 68 | grep " $(readlink -f $1) " /proc/mounts | grep " rw," 2>/dev/null 69 | return $? 70 | } 71 | 72 | mount_rw() { 73 | mount -o remount,rw $1 74 | DID_MOUNT_RW=$1 75 | } 76 | 77 | unmount_rw() { 78 | if [ "x$DID_MOUNT_RW" = "x$1" ]; then 79 | mount -o remount,ro $1 80 | fi 81 | } 82 | 83 | set_perms() { 84 | echo "\nSetting permissions..." 85 | chmod 644 $STEPDIR/* 86 | chown 0:2000 $STEPDIR 87 | if [ ! "$1" == "com.android.systemui" ]; then 88 | chmod 755 $MODDIR/system/etc/ 89 | chmod 644 $PERMISSIONXMLDIR/* 90 | chmod 644 $WHITELISTXMLDIR/* 91 | chmod 755 $SYSTEMIZE_TARGET/* 92 | chmod 644 $SYSTEMIZE_TARGET/*/* 93 | fi 94 | } 95 | 96 | get_logs() { 97 | echo "$(cmd overlay dump)" >$MODDIR/logs/$ID-overlays.log 98 | echo "---Device Info---" >$MODDIR/logs/$ID-vars.log 99 | grep "^ro.product.device[^#]" /system/build.prop | 100 | sed 's/ro.product.device/DeviceCode/g' >>$MODDIR/logs/$ID-vars.log 101 | grep "^ro.product.model[^#]" /system/build.prop | 102 | sed 's/ro.product.model/DeviceName/g' >>$MODDIR/logs/$ID-vars.log 103 | grep "^ro.build.type[^#]" /system/build.prop | 104 | sed 's/ro.build.type/BuildType/g' >>$MODDIR/logs/$ID-vars.log 105 | grep "^ro.build.version.security_patch[^#]" /system/build.prop | 106 | sed 's/ro.build.version.security_patch/SecurityPatch/g' \ 107 | >>$MODDIR/logs/$ID-vars.log 108 | grep "^ro.product.cpu.abilist[^#]" /system/build.prop | 109 | sed 's/ro.product.cpu.abilist/Arch/g' >>$MODDIR/logs/$ID-vars.log 110 | grep "^ro.build.version.sdk[^#]" /system/build.prop | 111 | sed 's/ro.build.version.sdk/APIVer/g' >>$MODDIR/logs/$ID-vars.log 112 | grep "^ro.build.flavor[^#]" /system/build.prop | 113 | sed 's/ro.build.flavor/BuildFlavor/g' >>$MODDIR/logs/$ID-vars.log 114 | echo "\n---ROM Info---" >>$MODDIR/logs/$ID-vars.log 115 | grep "^ro.build.host[^#]" /system/build.prop | 116 | sed 's/ro.build.host/Host/g' >>$MODDIR/logs/$ID-vars.log 117 | grep "^ro.*.device[^#]" /system/build.prop >>$MODDIR/logs/$ID-vars.log 118 | echo -e "\n---Variables---" >>$MODDIR/logs/$ID-vars.log 119 | ( 120 | set -o posix 121 | set 122 | ) >>$MODDIR/logs/$ID-vars.log 123 | if [ -z "$KSU" ] && [ -z "$APATCH" ]; then 124 | echo -e "\n---Magisk Version---" >>$MODDIR/logs/$ID-vars.log 125 | echo $(grep "MAGISK_VER_CODE=" /data/adb/magisk/util_functions.sh | 126 | sed "s/MAGISK_VER_CODE/MagiskVersion/") >>$MODDIR/logs/$ID-vars.log 127 | elif [ "$APATCH" ]; then 128 | echo -e "\n---APATCH Version---\nAPVersion=$APATCH_VER" >>$MODDIR/logs/$ID-vars.log 129 | else 130 | echo -e "\n---KSU Version---\nKSUVersion=$KSU_VER" >>$MODDIR/logs/$ID-vars.log 131 | fi 132 | echo -e "\n---Module Version---" >>$MODDIR/logs/$ID-vars.log 133 | echo $(grep "versionCode=" $MODDIR/module.prop) >>$MODDIR/logs/$ID-vars.log 134 | find $MODDIR >$MODDIR/logs/find.log 135 | 136 | rm -rf /storage/emulated/0/Documents/$ID/* 137 | mkdir -p /storage/emulated/0/Documents/$ID 138 | cp -rf $MODDIR/logs/* /storage/emulated/0/Documents/$ID/ 139 | echo "\nLogs copied to /sdcard/Documents/$ID..." 140 | } 141 | 142 | setvars() { 143 | if [ -z "$MAGIC_MOUNT" ]; then 144 | STEPDIRPREFIX=$MODDIR 145 | else 146 | STEPDIRPREFIX=$MODDIR/system 147 | fi 148 | SUFFIX="/overlay/QuickSwitchOverlay" 149 | if [ "$API" -ge 29 ]; then 150 | STEPDIR=$STEPDIRPREFIX/product$SUFFIX 151 | case "$(getprop ro.product.brand) $(getprop ro.product.manufacturer)" in 152 | *samsung*) 153 | if [ ! -d /product/overlay ]; then 154 | STEPDIR=$STEPDIRPREFIX/vendor$SUFFIX 155 | fi 156 | ;; 157 | *OnePlus*) 158 | if [ "$API" -ge 31 ]; then 159 | if [ -d /system_ext/oplus ]; then 160 | STEPDIR=$STEPDIRPREFIX/vendor$SUFFIX 161 | else 162 | STEPDIR=$STEPDIRPREFIX/product$SUFFIX 163 | fi 164 | fi 165 | ;; 166 | *) 167 | PRODUCT=true 168 | if [ -z "$KSU" ] && [ -z "$APATCH" ]; then 169 | # Yay, magisk supports bind mounting /product now 170 | MAGISK_VER_CODE=$(grep "MAGISK_VER_CODE=" /data/adb/magisk/util_functions.sh | awk -F = '{ print $2 }') 171 | if [ $MAGISK_VER_CODE -ge "20000" ]; then 172 | STEPDIR=$STEPDIRPREFIX/product$SUFFIX 173 | else 174 | abort "Magisk v20 is required for users on Android 10" 175 | abortexit "Please update Magisk and try again." 176 | fi 177 | else 178 | STEPDIR=$STEPDIRPREFIX/product$SUFFIX 179 | fi 180 | ;; 181 | esac 182 | else 183 | SUFFIX="/overlay" 184 | if [ -d /oem/OP -o -d /OP ]; then 185 | case "$(getprop ro.product.manufacturer)" in 186 | LGE) 187 | OEM=true 188 | mkdir -p $MODDIR/$defaultlauncherPath 189 | touch $MODDIR/$defaultlauncherPath/.replace 190 | if [ -d /oem/OP ]; then 191 | STEPDIR=/oem/OP/OPEN_*/overlay/framework 192 | is_mounted " /oem" || mount /oem 193 | is_mounted_rw " /oem" || mount_rw /oem 194 | is_mounted " /oem/OP" || mount /oem/OP 195 | is_mounted_rw " /oem/OP" || mount_rw /oem/OP 196 | elif [ -d /OP ]; then 197 | STEPDIR=/OP/OPEN_*/overlay/framework 198 | is_mounted " /OP" || mount /OP 199 | is_mounted_rw " /OP" || mount_rw /OP 200 | fi 201 | # globs don't like to be quoted so we have to set the variable again without quotes first. 202 | STEPDIR=$STEPDIR 203 | ;; 204 | esac 205 | else 206 | PRODUCT= 207 | OEM= 208 | STEPDIR=$STEPDIRPREFIX/vendor$SUFFIX 209 | fi 210 | fi 211 | 212 | # Assign misc variables 213 | PERMISSIONXMLDIR=$MODDIR/system/etc/permissions 214 | WHITELISTXMLDIR=$MODDIR/system/etc/sysconfig 215 | SYSTEMIZE_TARGET=$MODDIR/system/priv-app 216 | } 217 | 218 | reset_provider() { 219 | setvars 220 | if [ "$OEM" ]; then 221 | rm -rf $STEPDIR/QuickSwitchOverlay.apk 222 | fi 223 | rm -rf $MODDIR/system 224 | if [ "$KSU" ] || [ "$APATCH" ] ; then 225 | [ -d $MODDIR/product ] && rm -rf $MODDIR/product 226 | [ -d $MODDIR/vendor ] && rm -rf $MODDIR/vendor 227 | fi 228 | rm -rf /data/resource-cache/overlays.list 229 | find /data/resource-cache/ -name "*QuickstepSwitcherOverlay*" -exec rm -rf {} \; 230 | find /data/resource-cache/ -name "*QuickSwitchOverlay*" -exec rm -rf {} \; 231 | sed -i 's/\(description=\[ Quickstep : \)[^]]*\]/\1Default ]/' $MODDIR/module.prop 232 | } 233 | 234 | unmount_rw_stepdir() { 235 | if [ "$OEM" ]; then 236 | is_mounted_rw " /oem" && unmount_rw /oem 237 | is_mounted_rw " /oem/OP" && unmount_rw /oem/OP 238 | is_mounted_rw " /OP" && unmount_rw /OP 239 | fi 240 | } 241 | 242 | check_package() { 243 | if [[ $(pm path $1 2>/dev/null) == "" ]]; then #Check that output of pm path is not empty, /dev/null because java throws an exception when it is empty. 244 | abortexit "\nPackagename is not correct or empty \nPlease enter it in the format --ch={packagename} \nFor example, for Lawnchair Beta3... \n/data/adb/modules/quickswitch/quickswitch --ch=app.lawnchair" 245 | fi 246 | } 247 | 248 | switch_providers() { 249 | reset_provider 250 | 251 | echo "\nThe overlay will be copied to $STEPDIR..." 252 | 253 | APKPATH=$(pm path $1 | sed "s|package:||" | sed -n '/base\.apk/p' ) 254 | 255 | # Create needed dirs 256 | while [ ! -d "$STEPDIR" ]; do 257 | setvars 258 | mkdir -p $STEPDIR 259 | done 260 | 261 | if [ ! "$1" == "com.android.systemui" ]; then 262 | mkdir -p $SYSTEMIZE_TARGET/QuickSwitch-${1}-${RAND} 263 | mkdir -p $MODDIR/system/etc/permissions 264 | mkdir -p $MODDIR/system/etc/sysconfig 265 | if [ ! -z "$DATAAPKPATH" ]; then 266 | # if this yeets /data/app this time then its @paphonb's fault xdd 267 | rm -rf $DATAAPKPATH 268 | fi 269 | if [ ! -z "$REPLACEPATH" ]; then 270 | mkdir -p ${MODDIR}${REPLACEPATH}/ 271 | touch ${MODDIR}${REPLACEPATH}/.replace 272 | fi 273 | cp -rf $APKPATH \ 274 | $SYSTEMIZE_TARGET/QuickSwitch-"${1}"-"${RAND}"/QuickSwitch-"${1}"-"${RAND}".apk 275 | 276 | perms=$(aapt2 d permissions $APKPATH | grep "uses-permission:" | 277 | sed -r "s|.*='(.*)'|\1|") 278 | permissions=$(echo "$perms" | 279 | sed -re "s| ||g; s|^| |") 280 | 281 | echo " 282 | 283 | 284 | $permissions 285 | 286 | " >$PERMISSIONXMLDIR/privapp-permissions-$1.xml 287 | 288 | echo " 289 | 290 | 291 | " >$WHITELISTXMLDIR/$1-hiddenapi-package-whitelist.xml 292 | 293 | # Get logs for the patching process if the selected launcher is OnePlus Launcher 294 | # If more launchers get patch functionality I'll make the package name a variable array which can easily be added to. 295 | if [ "$1" == "net.oneplus.launcher" ]; then 296 | echo $(logcat -d | grep "I Patcher") >$MODDIR/logs/$ID-Patcher.log 297 | elif [ -f "$MODDIR/logs/$ID-Patcher.log" ]; then 298 | rm $MODDIR/logs/$ID-Patcher.log 299 | fi 300 | fi 301 | 302 | echo " 303 | 304 | $1/com.android.quickstep.RecentsActivity 305 | " >$OVERLAYDIR/overlay/values/strings.xml 306 | 307 | if [ ! -z "$PLATFORMSIGNATURE" ]; then 308 | cat "$PLATFORMSIGNATURE" >$OVERLAYDIR/overlay/values/arrays.xml 309 | fi 310 | 311 | rm -rf ${MODDIR}/compiled 312 | mkdir ${MODDIR}/compiled 313 | aapt2 compile -v --dir ${OVERLAYDIR}/overlay/ -o ${MODDIR}/compiled && \ 314 | aapt2 link -o ${MODDIR}/unsigned.apk -I /system/framework/framework-res.apk \ 315 | --manifest ${OVERLAYDIR}/AndroidManifest.xml ${MODDIR}/compiled/* \ 316 | &>$MODDIR/logs/aapt2.log 317 | rm -rf ${MODDIR}/compiled 318 | 319 | if [ -s ${MODDIR}/unsigned.apk ]; then 320 | sign ${MODDIR}/unsigned.apk ${MODDIR}/signed.apk 321 | cp -rf ${MODDIR}/signed.apk ${STEPDIR}/QuickSwitchOverlay.apk 322 | [ ! -s ${MODDIR}/signed.apk ] && cp -rf ${MODDIR}/unsigned.apk ${STEPDIR}/QuickSwitchOverlay.apk 323 | rm -rf ${MODDIR}/signed.apk ${MODDIR}/unsigned.apk 324 | else 325 | abort "Overlay not created!" 326 | abort "This is generally a rom incompatibility," 327 | abortexit "currently I'm unsure how to fix this." 328 | fi 329 | 330 | if [ -s ${STEPDIR}/QuickSwitchOverlay.apk ]; then 331 | echo "\nOverlay successfully copied..." 332 | sed -i "s/\(description=\[ Quickstep : \)[^]]*\]/\1✅$1 ]/" $MODDIR/module.prop 333 | else 334 | abortexit "The overlay was not copied, please send logs to the developer." 335 | fi 336 | 337 | set_perms $1 338 | 339 | # Lets save this for a rainy day 340 | # SDK=$(getprop ro.build.version.sdk) 341 | # SAMMY=$(getprop ro.product.brand) 342 | 343 | # if [[ "$SDK" = 29 ]] && \ 344 | # [[ "$SAMMY" == *"samsung"* ]]; then 345 | # for i in $(cmd overlay list | grep navbar | awk '{print $NF}' | \ 346 | # egrep -v com.samsung.internal.systemui.navbar.gestural_no_hint); do 347 | # cmd overlay disable $i 348 | # done 349 | # cmd overlay enable com.samsung.internal.systemui.navbar.gestural_no_hint 350 | # fi 351 | 352 | # lets not add this just yet as it can cause crashes in certain circumstances 353 | # if [ ! "$1" == "com.android.systemui" ]; then 354 | # echo "pm set-home-activity $1" > $MODDIR/service.sh 355 | # echo 'rm -rf $0' >> $MODDIR/service.sh 356 | # fi 357 | } 358 | 359 | print_modname() { 360 | # @Skittles9823 made this ascii and is way to proud of it 361 | echo " " 362 | echo " _____ " 363 | echo " __ | | __ " 364 | echo " | || || | " 365 | echo " | || || | " 366 | echo " |__|| ||__| " 367 | echo " |_____| " 368 | echo " QuickSwitch " 369 | echo " " 370 | echo " The Lawnchair Team" 371 | echo " " 372 | } 373 | 374 | print_modname 375 | 376 | resetprop ro.recents.grid false 377 | sed -i "/ro.recents.grid*/d" $MODDIR/system.prop 378 | 379 | for arg in $(echo ${@}); do 380 | args=$(echo $arg | tr '=' ' ') 381 | opt1=$(echo $args | awk '{ print $1 }') 382 | opt2=$(echo $args | awk '{ print $2 }') 383 | case "$opt1" in 384 | --reset) 385 | reset_provider 386 | echo "The recents provider has been reset to default." 387 | continue 388 | ;; 389 | --uninstall) 390 | reset_provider 391 | continue 392 | ;; 393 | --grid) 394 | resetprop ro.recents.grid true 395 | echo "ro.recents.grid=true" >>$MODDIR/system.prop 396 | continue 397 | ;; 398 | -ch|--ch) 399 | check_package "$opt2" 400 | switch_providers "$opt2" 401 | continue 402 | ;; 403 | esac 404 | done 405 | 406 | # If the script is called, then there is no reason for the module to be disabled. 407 | # Let's make sure the module is enabled. 408 | rm -rf $MODDIR/disable 409 | 410 | unmount_rw_stepdir 411 | 412 | get_logs 413 | 414 | echo "\nPlease reboot for changes to take effect." 415 | 416 | exit $? 417 | -------------------------------------------------------------------------------- /module/sepolicy.rule: -------------------------------------------------------------------------------- 1 | allow system_server untrusted_app_all_devpts chr_file { read write } 2 | -------------------------------------------------------------------------------- /module/service.sh: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | 3 | # Late props which must be set after boot_completed 4 | { 5 | until [[ "$(getprop sys.boot_completed)" == "1" ]]; do 6 | sleep 1 7 | done 8 | 9 | # avoid breaking OnePlus display modes/fingerprint scanners 10 | resetprop vendor.boot.verifiedbootstate green 11 | }& 12 | -------------------------------------------------------------------------------- /module/system.prop: -------------------------------------------------------------------------------- 1 | ro.boot.vendor.overlay.static=false 2 | -------------------------------------------------------------------------------- /module/system/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j7b3y/QuickSwitch/7f08bdbca7075c5ae29848c885808c5f82db402f/module/system/placeholder -------------------------------------------------------------------------------- /module/uninstall.sh: -------------------------------------------------------------------------------- 1 | ./$(dirname $0)/quickswitch --uninstall 2 | -------------------------------------------------------------------------------- /module/zipsigner: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | # zipsigner: wrapper to set up and run zipsigner.jar from terminal 3 | # osm0sis @ xda-developers 4 | 5 | case $# in 6 | 1 | 3 | 5) 7 | # when one argument less than needed assume the output name is missing and automatically set it to *-signed.* 8 | eval n=\${$#} 9 | set -- "$@" "$(echo $n | sed 's/\(.*\)\./\1-signed\./')" 10 | ;; 11 | esac 12 | 13 | if [ "$USER" == "root" ]; then 14 | dir="$( 15 | cd "$(dirname "$0")" 16 | pwd 17 | )" 18 | /apex/com.android.art/bin/dalvikvm -Djava.io.tmpdir=. -Xnodex2oat -Xnoimage-dex2oat -cp $dir/zipsigner-*.jar com.topjohnwu.utils.ZipSigner "$@" 2>/dev/null \ 19 | || /apex/com.android.art/bin/dalvikvm -Djava.io.tmpdir=. -Xnoimage-dex2oat -cp $dir/zipsigner-*.jar com.topjohnwu.utils.ZipSigner "$@" 20 | else 21 | echo "zipsigner: need root permissions" 22 | fi 23 | -------------------------------------------------------------------------------- /module/zipsigner-3.0-dexed.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/j7b3y/QuickSwitch/7f08bdbca7075c5ae29848c885808c5f82db402f/module/zipsigner-3.0-dexed.jar -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | cd module && zip -r ../QuickSwitch-fork.zip $(cat ../files.txt) 2 | cd ../withoutui && zip -r -u ../QuickSwitch-fork.zip module.prop 3 | 4 | cd ../module && zip -r ../QuickSwitch-fork-webui $(cat ../files.txt) 5 | cd ../webui && zip -r -u ../QuickSwitch-fork-webui module.prop webroot* -------------------------------------------------------------------------------- /webui/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # QuickSwitch - Quickstep enabler for supported launchers 2 | 3 | QuickSwitch is a Magisk module which systemlessly allows supported launchers to access the recents (QuickStep) APIs 4 | 5 | ## Changelog: 6 | 7 | ### 4.0.4 8 | 9 | - Add WebUI 10 | - Compatible with Magic Mount(Apatch,KernelSU Next) -------------------------------------------------------------------------------- /webui/module.prop: -------------------------------------------------------------------------------- 1 | id=quickswitch 2 | name=QuickSwitch Fork(WebUI) 3 | version=v4.0.4f-webui 4 | versionCode=4040 5 | author=The Lawnchair team 6 | description=[ Quickstep : Default ] Quickstep enabler for supported launchers 7 | updateJson=https://raw.githubusercontent.com/j7b3y/QuickSwitch/refs/heads/anyfix/master/wiwebui/update.json -------------------------------------------------------------------------------- /webui/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v4.0.4f-webui", 3 | "versionCode": 4040, 4 | "zipUrl": "https://github.com/j7b3y/QuickSwitch/releases/latest/download/QuickSwitch-fork-webui.zip", 5 | "changelog": "https://raw.githubusercontent.com/j7b3y/QuickSwitch/refs/heads/anyfix/master/webui/CHANGELOG.md" 6 | } -------------------------------------------------------------------------------- /webui/webroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebUI 8 | 9 | 10 |
11 |

QuickSwitch

12 |
13 | 14 |
15 | 16 |
17 |

Dashboard

18 |
19 |
20 | Module Version 21 | Loading... 22 |
23 |
24 | Root 25 | Loading... 26 |
27 |
28 | Set Launcher 29 | Loading... 30 |
31 |
32 |
33 | 34 |
35 |

Launchers

36 |
37 |
38 | 39 |
40 |
41 | 42 | 52 | 53 |
54 | 59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /webui/webroot/modules/dashboard.js: -------------------------------------------------------------------------------- 1 | import {run} from "./exec.js"; 2 | 3 | async function getmodver() { 4 | const modver = await run("grep '^version=' module.prop | cut -d'=' -f2") 5 | return modver; 6 | } 7 | 8 | async function getroot() { 9 | try { 10 | const root = await run("readlink -f ./busybox"); 11 | if (root.match(/magisk/)) { 12 | return "Magisk"; 13 | } else if (root.match(/ksu/)) { 14 | return "KernelSU"; 15 | } else if (root.match(/ap/)) { 16 | return "Apatch"; 17 | } 18 | } catch (error) { 19 | return "Unknown"; 20 | } 21 | } 22 | 23 | async function getlauncher() { 24 | const launcher = await run("grep '^description=' module.prop | sed -E 's/.*\\[ Quickstep : ([^ ]+) \\].*/\\1/' | sed 's/✅//g'") 25 | return launcher; 26 | } 27 | 28 | async function updateDashboardValues() { 29 | document.getElementById("modver").textContent = await getmodver(); 30 | document.getElementById("root").textContent = await getroot(); 31 | document.getElementById("launcher").textContent = await getlauncher(); 32 | } 33 | 34 | document.addEventListener("DOMContentLoaded", await updateDashboardValues); -------------------------------------------------------------------------------- /webui/webroot/modules/exec.js: -------------------------------------------------------------------------------- 1 | import {exec ,toast } from './kernelsu.js'; 2 | 3 | export async function run(cmd) { 4 | const { errno, stdout, stderr } = await exec(cmd,{ cwd: '/data/adb/modules/quickswitch' }); 5 | if (errno != 0) { 6 | toast(`stderr: ${stderr}`); 7 | return undefined; 8 | } else { 9 | return stdout; 10 | } 11 | } -------------------------------------------------------------------------------- /webui/webroot/modules/kernelsu.js: -------------------------------------------------------------------------------- 1 | let callbackCounter = 0; 2 | function getUniqueCallbackName(prefix) { 3 | return `${prefix}_callback_${Date.now()}_${callbackCounter++}`; 4 | } 5 | 6 | export function exec(command, options) { 7 | if (typeof options === "undefined") { 8 | options = {}; 9 | } 10 | 11 | return new Promise((resolve, reject) => { 12 | 13 | const callbackFuncName = getUniqueCallbackName("exec"); 14 | 15 | window[callbackFuncName] = (errno, stdout, stderr) => { 16 | resolve({ errno, stdout, stderr }); 17 | cleanup(callbackFuncName); 18 | }; 19 | 20 | function cleanup(successName) { 21 | delete window[successName]; 22 | } 23 | 24 | try { 25 | ksu.exec(command, JSON.stringify(options), callbackFuncName); 26 | } catch (error) { 27 | reject(error); 28 | cleanup(callbackFuncName); 29 | } 30 | }); 31 | } 32 | 33 | function Stdio() { 34 | this.listeners = {}; 35 | } 36 | 37 | Stdio.prototype.on = function (event, listener) { 38 | if (!this.listeners[event]) { 39 | this.listeners[event] = []; 40 | } 41 | this.listeners[event].push(listener); 42 | }; 43 | 44 | Stdio.prototype.emit = function (event, ...args) { 45 | if (this.listeners[event]) { 46 | this.listeners[event].forEach((listener) => listener(...args)); 47 | } 48 | }; 49 | 50 | function ChildProcess() { 51 | this.listeners = {}; 52 | this.stdin = new Stdio(); 53 | this.stdout = new Stdio(); 54 | this.stderr = new Stdio(); 55 | } 56 | 57 | ChildProcess.prototype.on = function (event, listener) { 58 | if (!this.listeners[event]) { 59 | this.listeners[event] = []; 60 | } 61 | this.listeners[event].push(listener); 62 | }; 63 | 64 | ChildProcess.prototype.emit = function (event, ...args) { 65 | if (this.listeners[event]) { 66 | this.listeners[event].forEach((listener) => listener(...args)); 67 | } 68 | }; 69 | 70 | export function spawn(command, args, options) { 71 | if (typeof args === "undefined") { 72 | args = []; 73 | } else if (typeof args === "object") { 74 | options = args; 75 | } 76 | 77 | if (typeof options === "undefined") { 78 | options = {}; 79 | } 80 | 81 | const child = new ChildProcess(); 82 | const childCallbackName = getUniqueCallbackName("spawn"); 83 | window[childCallbackName] = child; 84 | 85 | function cleanup(name) { 86 | delete window[name]; 87 | } 88 | 89 | child.on("exit", code => { 90 | cleanup(childCallbackName); 91 | }); 92 | 93 | try { 94 | ksu.spawn( 95 | command, 96 | JSON.stringify(args), 97 | JSON.stringify(options), 98 | childCallbackName 99 | ); 100 | } catch (error) { 101 | child.emit("error", error); 102 | cleanup(childCallbackName); 103 | } 104 | return child; 105 | } 106 | 107 | export function fullScreen(isFullScreen) { 108 | ksu.fullScreen(isFullScreen); 109 | } 110 | 111 | export function toast(message) { 112 | ksu.toast(message); 113 | } -------------------------------------------------------------------------------- /webui/webroot/modules/launchers.js: -------------------------------------------------------------------------------- 1 | import { run } from './exec.js'; 2 | 3 | async function getQuicksteps() { 4 | const qslist = JSON.parse(await run('sh webroot/scripts/genQuickstepList.sh')); 5 | return qslist; 6 | } 7 | 8 | function handleCheckboxChange(event) { 9 | const checkboxes = document.querySelectorAll('#launchersBox input[type="checkbox"]'); 10 | 11 | checkboxes.forEach(checkbox => { 12 | if (checkbox !== event.target) { 13 | checkbox.checked = false; 14 | } 15 | }); 16 | 17 | const setOverlayButton = document.getElementById("setOverlay"); 18 | setOverlayButton.textContent = event.target.checked ? "Set" : "Reset"; 19 | 20 | setOverlayButton.dataset.launcherName = event.target.checked ? event.target.parentElement.previousElementSibling.textContent : ''; 21 | } 22 | 23 | async function genList() { 24 | const appList = await getQuicksteps(); 25 | const launchersBox = document.getElementById("launchersBox"); 26 | launchersBox.innerHTML = ""; 27 | 28 | appList.forEach(app => { 29 | const launcherContainer = document.createElement("div"); 30 | launcherContainer.className = "launcher-container"; 31 | 32 | const launcherName = document.createElement("span"); 33 | launcherName.className = "launchername"; 34 | launcherName.textContent = app; 35 | 36 | const checkbox = document.createElement("span"); 37 | checkbox.className = "checkbox"; 38 | checkbox.innerHTML = ''; 39 | 40 | launcherContainer.appendChild(launcherName); 41 | launcherContainer.appendChild(checkbox); 42 | 43 | checkbox.querySelector('input').addEventListener('change', (event) => { 44 | handleCheckboxChange(event); 45 | }); 46 | 47 | launchersBox.appendChild(launcherContainer); 48 | }); 49 | }; 50 | 51 | await genList() 52 | 53 | document.addEventListener("DOMContentLoaded", async () => { 54 | }); -------------------------------------------------------------------------------- /webui/webroot/modules/loader.js: -------------------------------------------------------------------------------- 1 | const launchers = document.getElementById("launchers"); 2 | 3 | async function loadModule(moduleName) { 4 | return new Promise((resolve, reject) => { 5 | const script = document.createElement('script'); 6 | script.type = 'module'; 7 | script.src = `modules/${moduleName}.js`; 8 | 9 | script.onload = () => resolve(); 10 | script.onerror = () => reject(new Error(`Failed to load module: ${moduleName}`)); 11 | 12 | document.body.appendChild(script); 13 | }); 14 | } 15 | 16 | launchers.addEventListener("click", async () => { 17 | const launchersBox = document.getElementById("launchersBox"); 18 | const loadingAnim = document.createElement("div"); 19 | loadingAnim.className = "loadingAnim"; 20 | launchersBox.appendChild(loadingAnim); 21 | 22 | await loadModule("launchers"); 23 | 24 | launchersBox.removeChild(loadingAnim); 25 | }); -------------------------------------------------------------------------------- /webui/webroot/modules/navbar.js: -------------------------------------------------------------------------------- 1 | function showContent(contentId) { 2 | 3 | document.querySelectorAll('.content').forEach(content => { 4 | content.classList.remove('active'); 5 | }); 6 | 7 | document.getElementById(contentId).classList.add('active'); 8 | 9 | document.querySelectorAll('.navbar a').forEach(link => { 10 | link.classList.remove('active'); 11 | }); 12 | event.currentTarget.classList.add('active'); 13 | } 14 | 15 | window.showContent = showContent; -------------------------------------------------------------------------------- /webui/webroot/modules/process.js: -------------------------------------------------------------------------------- 1 | import { run } from './exec.js'; 2 | 3 | const setOverlayButton = document.getElementById("setOverlay"); 4 | const overlay = document.getElementById("overlay"); 5 | const closeButton = document.getElementById("closeButton"); 6 | const rebootButton = document.getElementById("rebootButton"); 7 | 8 | setOverlayButton.addEventListener("click", async () => { 9 | const checkedCheckbox = document.querySelector('#launchersBox input[type="checkbox"]:checked'); 10 | overlay.style.display = "flex"; 11 | logging(); 12 | 13 | try { 14 | let qscommand; 15 | if (checkedCheckbox) { 16 | qscommand = `su -c sh /data/adb/modules/quickswitch/quickswitch --ch=${checkedCheckbox.closest('.launcher-container').querySelector('.launchername').textContent}`; 17 | } else { 18 | qscommand = `su -c sh /data/adb/modules/quickswitch/quickswitch --reset`; 19 | } 20 | logging(`Processing command: ${qscommand}\n`); 21 | const result = await run(`${qscommand}`); 22 | 23 | if (result !== undefined) { 24 | logging(result); 25 | } else { 26 | logging('Command executed with errors or returned undefined.'); 27 | } 28 | 29 | } catch (error) { 30 | logging( `Error: ${error.message}`); 31 | } 32 | }); 33 | 34 | function logging(log) { 35 | const logContent = document.getElementById("logContent"); 36 | if (typeof log === "undefined") { 37 | logContent.textContent = ""; 38 | } else { 39 | logContent.textContent += log + '\n'; 40 | } 41 | } 42 | 43 | closeButton.addEventListener("click", () => { 44 | overlay.style.display = "none"; 45 | }); 46 | 47 | rebootButton.addEventListener("click", async () => { 48 | await run(`reboot`) 49 | }); 50 | 51 | document.addEventListener("DOMContentLoaded", async () => { 52 | }); -------------------------------------------------------------------------------- /webui/webroot/scripts/genQuickstepList.sh: -------------------------------------------------------------------------------- 1 | pm list packages -e --user 0 | sed 's/package://' | ./busybox xargs -n 1 -P 8 sh -c ' 2 | for pkg; do 3 | if dumpsys package "$pkg" 2>/dev/null | grep -q "android.intent.category.LAUNCHER_APP"; then 4 | echo "\"$pkg\"" 5 | fi 6 | done 7 | ' _ | awk 'BEGIN {print "["} {if (NR > 1) printf ", "; printf "%s", $0} END {print "]"}' -------------------------------------------------------------------------------- /webui/webroot/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #141414; 3 | color: #f5f5f5; 4 | } 5 | 6 | /* header */ 7 | 8 | .titleheader { 9 | padding: 3px; 10 | display:flex; 11 | align-items: center; 12 | justify-content: space-between; 13 | position: relative; 14 | } 15 | 16 | .titleheader h3 { 17 | position: absolute; 18 | left: 50%; 19 | transform: translateX(-50%); 20 | } 21 | 22 | #reloadButton { 23 | background-color: #007bff; 24 | color: white; 25 | border: none; 26 | border-radius: 5px; 27 | padding: 5px 10px; 28 | cursor: pointer; 29 | font-size: 14px; 30 | width: 40px; 31 | } 32 | 33 | #reloadButton:hover { 34 | background-color: #0056b3; 35 | } 36 | 37 | /* navbar */ 38 | 39 | .navbar { 40 | position: fixed; 41 | bottom: 0; 42 | left: 0; 43 | width: 100%; 44 | height: 60px; 45 | background-color: #333; 46 | display: flex; 47 | justify-content: space-around; 48 | align-items: center; 49 | color: #fff; 50 | font-size: 14px; 51 | box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.5); 52 | } 53 | 54 | .navbar a { 55 | color: white; 56 | text-decoration: none; 57 | display: flex; 58 | flex-direction: column; 59 | align-items: center; 60 | flex: 1; 61 | padding: 10px; 62 | } 63 | 64 | .navbar a .icon { 65 | font-size: 24px; 66 | margin-bottom: 4px; 67 | } 68 | 69 | .navbar a:hover { 70 | background-color: #555; 71 | } 72 | 73 | .navbar a.active { 74 | background-color: #007bff; 75 | } 76 | 77 | .content { 78 | padding: 20px; 79 | display: none; 80 | } 81 | 82 | .content.active { 83 | display: block; 84 | } 85 | 86 | /* dashboard */ 87 | 88 | #dashboardContent { 89 | background-color: #242424; 90 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 91 | border-radius: 10px; 92 | } 93 | 94 | .status-box { 95 | background-color: #333; 96 | padding: 15px; 97 | border-radius: 8px; 98 | } 99 | 100 | .status-container { 101 | display: flex; 102 | justify-content: space-between; 103 | padding: 8px 0; 104 | border-bottom: 1px solid #444; 105 | } 106 | 107 | .status-container:last-child { 108 | border-bottom: none; 109 | } 110 | 111 | .label { 112 | text-align: left; 113 | font-weight: bold; 114 | } 115 | 116 | .value { 117 | text-align: right; 118 | color: #ccc; 119 | } 120 | 121 | /* Launchers */ 122 | 123 | #launchersContent { 124 | background-color: #242424; 125 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 126 | border-radius: 10px; 127 | } 128 | 129 | .launchersBox { 130 | background-color: #333; 131 | padding: 15px; 132 | border-radius: 8px; 133 | } 134 | 135 | .launcher-container { 136 | display: flex; 137 | align-items: center; 138 | justify-content: space-between; 139 | padding: 8px 0; 140 | border-bottom: 1px solid #444; 141 | } 142 | 143 | .launcher-container:last-child { 144 | border-bottom: none; 145 | } 146 | 147 | .setOverlayButton { 148 | padding: 3px; 149 | display:flex; 150 | justify-content: flex-end; 151 | } 152 | 153 | #setOverlay { 154 | background-color: #007bff; 155 | color: white; 156 | border: none; 157 | border-radius: 5px; 158 | padding: 5px 10px; 159 | cursor: pointer; 160 | font-size: 14px; 161 | min-width: 60px; 162 | text-align: center; 163 | } 164 | 165 | #setOverlay:hover { 166 | background-color: #0056b3; 167 | } 168 | 169 | .loadingAnim { 170 | align-items: center; 171 | display: flex; 172 | } 173 | 174 | .loadingAnim::after { 175 | animation: loadingAnim 0.5s linear infinite; /* Use the correct keyframes name */ 176 | border: 1px solid rgb(255, 255, 255); 177 | border-radius: 50%; 178 | border-right: 1px solid rgba(255, 255, 255, 0.2); /* Corrected rgba syntax */ 179 | border-top: 1px solid rgba(255, 255, 255, 0.2); /* Corrected rgba syntax */ 180 | content: ""; 181 | width: 20px; /* Set width for the loading animation */ 182 | height: 20px; /* Set height for the loading animation */ 183 | margin: auto; /* Center the loading animation */ 184 | } 185 | 186 | @keyframes loadingAnim { 187 | 0% { 188 | transform: rotate(0); 189 | } 190 | 191 | 100% { 192 | transform: rotate(360deg); 193 | } 194 | } 195 | 196 | /* Overlay Window */ 197 | 198 | .overlay { 199 | display: none; 200 | position: fixed; 201 | top: 0; 202 | left: 0; 203 | width: 100%; 204 | height: 100%; 205 | background: rgba(0, 0, 0, 0.6); 206 | justify-content: center; 207 | align-items: center; 208 | z-index: 1000; 209 | } 210 | 211 | .modal-window { 212 | background-color: #242424; 213 | padding: 20px; 214 | border-radius: 8px; 215 | width: 80%; 216 | max-width: 500px; 217 | box-shadow: 0 0 15px rgba(0, 0, 0, 0.3); 218 | text-align: center; 219 | } 220 | 221 | .log-content { 222 | background-color: #000000; 223 | color: #ffffff; 224 | padding: 15px; 225 | border-radius: 5px; 226 | max-height: 300px; 227 | overflow-y: auto; 228 | margin-bottom: 15px; 229 | text-align: left; 230 | } 231 | 232 | #closeButton { 233 | background-color: #007bff; 234 | color: white; 235 | border: none; 236 | border-radius: 5px; 237 | padding: 10px 20px; 238 | cursor: pointer; 239 | } 240 | 241 | #closeButton:hover { 242 | background-color: #0056b3; 243 | } 244 | 245 | #rebootButton { 246 | background-color: #ff0000; 247 | color: white; 248 | border: none; 249 | border-radius: 5px; 250 | padding: 10px 20px; 251 | cursor: pointer; 252 | } 253 | 254 | #rebootButton:hover { 255 | background-color: #913030; 256 | } -------------------------------------------------------------------------------- /withoutui/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # QuickSwitch - Quickstep enabler for supported launchers 2 | 3 | QuickSwitch is a Magisk module which systemlessly allows supported launchers to access the recents (QuickStep) APIs 4 | 5 | ## Changelog: 6 | 7 | ### 4.0.4 8 | 9 | - Compatible with Magic Mount(Apatch,KernelSU Next) 10 | 11 | ### 4.0.3 12 | 13 | - Various fixes for KSU/Apatch by @j7b3y, @DrDisagree, and @ammargitham 14 | 15 | ### 4.0.2 16 | 17 | - unrevert embed framework-res 18 | - add sepolicy to fix `pm path` (Thanks Jan from the telegram group) 19 | 20 | ### v4.0.1 21 | 22 | - Revert embed framework-res 23 | - I forgor to add some KSU stuff from the 3.3.7 test zips 24 | 25 | ### v4.0.0 26 | 27 | - add KernelSU support 28 | - potential fix for appt not working 29 | - logging overlays now works correctly 30 | - raise overlay priority back to 9999 (oops, lowered it too much) 31 | - Build Overlay using embedded Framework (fixes overlay creation. By [DanGLES3](https://github.com/DanGLES3)) 32 | -------------------------------------------------------------------------------- /withoutui/module.prop: -------------------------------------------------------------------------------- 1 | id=quickswitch 2 | name=QuickSwitch Fork 3 | version=v4.0.4f 4 | versionCode=4040 5 | author=The Lawnchair team 6 | description=[ Quickstep : Default ] Quickstep enabler for supported launchers 7 | updateJson=https://raw.githubusercontent.com/j7b3y/QuickSwitch/refs/heads/anyfix/master/withoutui/update.json 8 | -------------------------------------------------------------------------------- /withoutui/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v4.0.4f", 3 | "versionCode": 4040, 4 | "zipUrl": "https://github.com/j7b3y/QuickSwitch/releases/latest/download/QuickSwitch-fork.zip", 5 | "changelog": "https://raw.githubusercontent.com/j7b3y/QuickSwitch/refs/heads/anyfix/master/withoutui/CHANGELOG.md" 6 | } 7 | --------------------------------------------------------------------------------