#!/system/bin/sh
# Terminal Magisk Mod Template
# by veez21 @ xda-developers
# Android 11 support by KreAch3R, jenslody @xda-developers

# Magisk Module ID **
ID="terminal_systemizer"

# Detect root
_name=$(basename $0)
ls /data >/dev/null 2>&1 || { echo "$ID needs to run as root!"; echo "type 'su' then '$_name'"; exit 1; }

# Magisk Mod Directory
MOUNTPATH="/data/adb/modules"
MODDIR="$MOUNTPATH/$ID"
[ ! -d $MODDIR ] && { echo "Module not detected!"; exit 1; }

# Load mod-util.sh
. $MODDIR/mod-util.sh || exit $?

# Set Log Files
mount -o remount,rw /cache 2>/dev/null
mount -o rw,remount /cache 2>/dev/null
# > Logs should go in this file
LOG=/data/local/tmp/$ID.log
oldLOG=/data/local/tmp/$ID-old.log
# > Verbose output goes here
VERLOG=/data/local/tmp/$ID-verbose.log
oldVERLOG=/data/local/tmp/$ID-verbose-old.log

# Start Logging verbosely
mv -f $VERLOG $oldVERLOG 2>/dev/null; mv -f $LOG $oldLOG 2>/dev/null
set -x 2>$VERLOG

# Main
# > You can start your MOD here.
# > You can add functions, variables & etc.
# > Rather than editing the default vars above.

help_me() {
  cat << EOF
$MODTITLE $VER($REL)
by $AUTHOR

Usage: $_name
   or: $_name [options]...
   
Options:
    -nc                    removes ANSI escape codes
    -f                     use 'Set SELinux' in the session for faster app listing
    -a [package name]      systemizes the package name(s) provided
    -d [apk dir]           systemizes the apk(s) provided
    -l                     list all systemized apps
    -h                     show this message
EOF
exit
}

use_aapt=false
export PATH=$MODDIR:$PATH
[[ "$(which aapt)" ]] && use_aapt=true
sysapp=0
privapp=0
IFUPDATED=""
perm_xml1=$SYSTEM/etc/permissions/privapp-permissions-platform.xml
perm_xml=$MODDIR$SYSTEM2/etc/permissions/privapp-permissions-platform.xml
[ -f $perm_xml1 ] && patch_xml=true || patch_xml=false
LISTFILE=$MODDIR/systemize.list
BOOTMODE=true
RESCAN=true
MERGEIMG=/data/adb/magisk_merge.img
use_cmd=false
se_set=false
faster_flag=false
[ -f $SYSTEM2/build.prop ] && SYSTEM=$SYSTEM2

# Method
if [ "$1" == "-f" ]; then
  use_cmd=true
  se_set=true
  faster_flag=true
  shift
elif [ "$(grep_prop selinux $MODDIR/module.prop)" == "true" ]; then
  use_cmd=true
  se_set=true
else
  [ $(getenforce) == "Permissive" ] && use_cmd=true || {
    [ $ANDROID_SOCKET_adbd ] && use_cmd=true || use_cmd=false
  }
fi
method="ls /data/data"
detect="codePath=$SYSTEM|targetSdk=0|codePath=/vendor/overlay"
if $use_cmd; then
  [ $API -le 23 ] && method="pm list packages -3"
  [ $API -ge 24 ] && method="cmd package list packages -3"
  detect="targetSdk=0"
fi
# Detect if /system/app is available
[ -d $SYSTEM/app ] && sysapp=1

# Detect if /system/priv-app is available
[ -d $SYSTEM/priv-app ] && privapp=1

# Detect free space
free_space="$(df -m $MOUNTPATH | tail -n1 | awk '{print $4}')"
total_space="$(df -m $MOUNTPATH | tail -n1 | awk '{print $2}')"

# List installed apps
list_installed_apps() {
  if $RESCAN; then
    if $se_set; then
      if [ "$(getenforce)" == "Enforcing" ]; then
        setenforce 0
      else
        se_set=false
      fi
    fi
    rm $LISTFILE 2>>$LOG; app=(); app1=(); app2=();
    for i in $($method | sed 's/.*://'); do
      h=$(((h+1)%4))
      Spinner "Loading Apps"
      if ! dumpsys package $i | grep -qEm1 -e "($detect)" - ; then
        app=${i##*/}
        package=$app
        package[c]=$package
        [ $API -le 30 ] && dir_app[c]=/data/app/$app-*/base.apk
        [ $API -ge 30 ] && dir_app[c]=/data/app/*/$package-*/base.apk
        $use_aapt && { app=$(aapt d badging ${dir_app[c]} | grep 'application-label:'); app=${app#*:}; }
        app1[c]=$(echo "$app" | tr ' ' '_' | tr -d "'")
        echo "app${c}^=${app1[c]}^=${dir_app[c]}^=${package[c]}^=" >> $LISTFILE
        c=$((c+1))
      fi
    done
    app2=($(echo "${app1[@]}" | sed "s/.*'//g"))
    app=($(printf '%s\n' "${app2[@]}" | sort -f))
    RESCAN=false
    $se_set && setenforce 1
  fi
}

# List systemized apps
list_systemized_apps() {
  systemized_apps=(); rev_dir=(); rev_label=(); rev_app=()
  [ "$tmpmodpath" ] && MODDIRS="$MODDIR $path" || MODDIRS="$MODDIR"
  systemized_apps=($(find $MODDIRS -name "*.apk" -type f))
  cc=1
  for i in ${systemized_apps[@]}; do
    ProgressBar $cc ${#systemized_apps[@]}
    rev_dir[cc]=${i%/*}
    rev_label=${i%/*}
    rev_label[cc]=${rev_label##*/}
    rev_app[cc]=${rev_label[cc]}
    $use_aapt && { rev_app=$(aapt d badging $i | grep 'application-label:'); rev_app=${rev_app#*:}; rev_app[cc]=$(echo $rev_app | tr ' ' '_' | tr -d "'"); }
    cc=$((cc+1))
  done
}

get_cfg() {
  APP_NAME=$(grep -wm1 "=${1}^" $LISTFILE | tr '^=' ' ' | awk '{print $2}') || abort "Package doesn't exist!"
  DIR_APP=$(grep -wm1 "=${1}^" $LISTFILE | tr '^=' ' ' | awk '{print $3}') || abort "Package doesn't exist!"
  PKG=$(grep -wm1 "=${1}^" $LISTFILE | tr '^=' ' ' | awk '{print $4}') || abort "Package doesn't exist!"
}

# Systemize function
# Usage: systemize <install directory> <apk directory>
systemize() {
  if [ "$2" ]; then
    apkdir="$(echo $2)"
    [ ! -f $apkdir ] && abort " $apkdir doesn't exist!"
    apk=${apkdir##*/}
    [ "$3" ] && PKG="$3"
    if [ "$PKG" ]; then
      name=$PKG
    else
      name=${apkdir%/*}; name=${name##*/}
      name=$(echo $name | tr -d ' ' | tr -d "'" | tr -d '*' | tr -d '-')
      $use_aapt && {
        name=$(aapt d badging $apkdir | head -n1 | awk '{print $2}')
        name=$(echo ${name#*=} | tr -d ' ' | tr -d "'")
      }
    fi
  fi
  chk_apk_size $apkdir
  echo " Transfering ${apk}(${name}) to '$1'..."
  if [ -d $SYSTEM/app/${name} ] || [ -d $SYSTEM/priv-app/${name} ] || [ -d $MODDIR$SYSTEM2/app/${name} ] || [ -d $MODDIR$SYSTEM2/priv-app/${name} ]; then
	$UNMOUNTIMG && {
	  unmount_magisk_img
	  IMG=$ORIGIMG
	  MOUNTPATH=$ORIGMOUNTPATH
	  MODDIR=$ORIGMODDIR
	}
    abort " App exists!"
  else
    mkdir -p ${MODDIR}${1}/${name}
    cp -f $apkdir ${MODDIR}${1}/${name}/${name}.apk
    if [ -d ${apkdir%/*}/lib ]; then
      echo " Transfering libs"
      cp -rf ${apkdir%/*}/lib ${MODDIR}${1}/${name}
    fi
    set_perm_recursive ${MODDIR}${1}/${name} 0 0 0755 0644
    $patch_xml && [ ${1##*/} == "priv-app" ] && {
      echo " Granting Permissions"
      mkdir -p $MODDIR$SYSTEM2/etc/permissions 2>/dev/null
      pp=0
      app_perm=(); perm=()
      for i in $(aapt d permissions $apkdir | grep -v 'package:' | awk '{print $2}'); do
        _perm=$(echo ${i#*=} | tr -d "'")
        perm[pp]=$_perm
        pp=$((pp+1))
      done
      app_perm=($(echo "${perm[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
      echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>
<!--
	Generated by $MODTITLE by $AUTHOR
-->
<permissions>
    <privapp-permissions package=\"${name}\">" >> $MODDIR$SYSTEM2/etc/permissions/privapp-permissions-${name}.xml
      for i in ${app_perm[@]}; do
        echo "        <permission name=\"$i\"/>" >> $MODDIR$SYSTEM2/etc/permissions/privapp-permissions-${name}.xml
      done
      echo "    </privapp-permissions>
</permissions>" >> $MODDIR$SYSTEM2/etc/permissions/privapp-permissions-${name}.xml
      chmod 644 $MODDIR$SYSTEM2/etc/permissions/privapp-permissions-${name}.xml
    }
    echo " ${name} - ${W}Done${N}"
	$UNMOUNTIMG && {
	  unmount_magisk_img
	  IMG=$ORIGIMG
	  MOUNTPATH=$ORIGMOUNTPATH
	  MODDIR=$ORIGMODDIR
	}
  fi
}

enter_apk_dir() {
  clear
  echo "Enter APK Path"
  echo "Example: ${W}/sdcard/launcher.apk${N}"
  echo " x - ${Bl}Back to Menu${N}"
  echo -n " > "
  read apkdir
  [ "$apkdir" == "x" ] && menu
  echo -n "Checking if file exists"
  if [ -f "$apkdir" ]; then
    echo " - OK"
    apk=${apkdir##*/}
    echo -n "Checking if file is an APK"
    if [ "${apk##*.}" == "apk" ]; then
      echo " - OK"
      echo -n "Checking package name"
      name=${apk%'.apk'*}
      if [ $use_aapt ]; then
        name=$(aapt d badging $apkdir | head -n1 | awk '{print $2}')
        name=${name#*=}
      fi
      name=$(echo $name | tr -d ' ' | tr -d "'")
      echo " - $name"
      if [ $privapp == 1 ] && [ $sysapp == 1 ]; then
        echo "Where to install?"
        echo -n "< 1 - $SYSTEM/app | 2 - $SYSTEM/priv-app | 0 - cancel > : "
        read loc
        if [ "$loc" == 1 ]; then
          systemize /system/app
        elif [ "$loc" == 2 ]; then
          systemize /system/priv-app
        elif [ "$loc" == 0 ]; then
          echo " $name canceled"; exit
        else
          abort "Invalid!"
        fi
      elif [ $sysapp == 1 ] && [ $privapp == 0 ]; then
        systemize /system/app
      elif [ $privapp == 1 ] && [ $sysapp == 0 ]; then
        systemize /system/priv-app
      else
        abort " - No installation directory!"
      fi
    else
      abort " - $apk is not an APK!"
    fi
  else
    abort " - $apkdir doesn't exist!"
  fi
}

enter_apk_name() {
  clear
  echo "Enter the App Label of the installed app"
  echo "Example: com.android.launcher"
  echo " x - ${Bl}Back to Menu${N}"
  echo -n " > "
  read apklabel
  if [ ! "$apklabel" ]; then
    abort "error"
  elif [ "$apklabel" == "x" ]; then
    menu
  fi
  if [ $privapp == 1 ] && [ $sysapp == 1 ]; then
    echo " Where to install?"
    echo -n " < 1 - $SYSTEM/app | 2 - $SYSTEM/priv-app | 0 - cancel > : "
    read loc
    if [ "$loc" == 1 ]; then
      installdir="app"
    elif [ "$loc" == 2 ]; then
      installdir="priv-app"
    elif [ "$loc" == 0 ]; then
      echo " $apklabel canceled"; exit
    else
      abort "Invalid!"
    fi
  elif [ -d $SYSTEM/priv-app ]; then
    installdir="priv-app"
  else
    installdir="app"
  fi
  [ $API -lt 30 ] && app_path="/data/app/${apklabel}-*/base.apk"
  [ $API -ge 30 ] && app_path="/data/app/*/${apklabel}-*/base.apk"
  systemize /system/$installdir "$app_path"
  [ $? -ne 0 ] && abort "Failed!"
}

# Merge
merge_img() {
  path=/dev/magisk_merge
  tmpmodpath=$path/${ID}
  [ ! "$ORIGIMG" ] && ORIGIMG=$IMG
  IMG=$MERGEIMG
  [ ! "$ORIGMOUNTPATH" ] && ORIGMOUNTPATH=$MOUNTPATH
  MOUNTPATH=$path
  [ ! "$ORIGMODDIR" ] && ORIGMODDIR=$MODDIR
  MODDIR=$tmpmodpath
  reqSizeM=$apk_sizeM
  mount_magisk_img
  cp -rf $ORIGMODDIR/. $MODDIR
}

# Unmount magisk img
unmount_magisk_img() {
  check_filesystem $IMG $MOUNTPATH
  newSizeM=$((curUsedM / 32 * 32 + 64))
  $MAGISKBIN/magisk imgtool umount $MOUNTPATH $MAGISKLOOP
  if [ $curSizeM -gt $newSizeM ]; then
    ui_print "- Shrinking $IMG to ${newSizeM}M"
    $MAGISKBIN/magisk imgtool resize $IMG $newSizeM >&2
  fi
}

# Check apk size
chk_apk_size() {
  dir_app=$(echo $1)
  free_space="$(df -m $MOUNTPATH | tail -n1 | awk '{print $3}')"
  total_space="$(df -m $MOUNTPATH | tail -n1 | awk '{print $2}')"
  apk_sizeM="$(du -m $dir_app | awk '{print $1}')"
  [ -d ${dir_app%/*}/lib ] && {
    lib_sizeM="$(du -ms ${dir_app%/*}/lib | awk '{print $1}')"
    apk_sizeM="$((apk_sizeM+lib_sizeM))"
  }
  UNMOUNTIMG=false
  if [ "$apk_sizeM" -gt "$((free_space-10))" ] && [ "$MOUNTPATH" == "/sbin/.magisk/img" ]; then
    echo " Checking App size - ${W}${apk_sizeM}M${N}"
    echo "- Insufficient $MOUNTPATH space!"
    echo "- APK size: ${apk_sizeM}M | $MOUNTPATH Free: ${free_space}M"
    echo "- Using magisk_merge.img in systemizing..."
    UNMOUNTIMG=true
    merge_img ${dir_app} 2>>$LOG
  elif [ -f "$MERGEIMG" ] && [ "$MOUNTPATH" == "/sbin/.magisk/img" ]; then
    echo "- Using magisk_merge.img in systemizing..."
    UNMOUNTIMG=true
    merge_img ${dir_app} 2>>$LOG
  else
    echo " Checking APK size - ${W}${apk_sizeM}M${N}"
  fi
}

mount -o remount,rw $MOUNTPATH 2>>$LOG

menu_list_systemized_apps() {
  clear
  list_installed_apps 2>>$LOG
  clear
  echo "$div"
  title_div "Installed Apps"
  echo "$div"
  echo ""
  for i in $(seq 0 $((${#app[@]}-1))); do
    echo " $((i+1)) - ${W}${app[i]}${N}" | tr '_' ' '
  done
  echo ""
  echo " ${Bl}To systemize multiple apps, enter multiple number. Example:${N} \"1 $(echo ${#app[$((${#app[@]}/3))]}) $(echo ${#app[$((${#app[@]}/2))]})\""
  echo " x - ${Bl}Back to Menu${N}"
  echo " r - ${Bl}Refresh list${N}"
  echo " 0 - ${Bl}Exit${N}"
  echo -n "[CHOICE]: "
  read installed
  [ "$installed" == "x" ] && menu
  [ "$installed" == "r" ] && RESCAN=true && menu_list_systemized_apps
  for i in $installed; do
    if [ "$i" -gt ${#app[@]} ] || [ "$i" -lt 0 ] || [ "$i" == "" ] || [ -n "$(echo $i | tr -d '0-9')" ] ; then
      abort "Invalid!"
    elif [ "$i" == 0 ]; then
      exit
    else
      echo "$div"
      i=$((i-1))
      echo "Systemizing ${W}${app[i]}${N}" | tr '_' ' '
      get_cfg ${app[i]}
      echo -n " Checking APK directory"
      if [ -f $DIR_APP ]; then
        echo " - ${W}OK${N}"
        apkdir=$DIR_APP
        apk=${apkdir##*/}
        name=${apkdir%/*}; name=${name##*/}; name=$(echo $name | tr -d ' ' | tr -d "'" | tr -d '*' | tr -d '-')
        echo " Checking package name - ${W}$PKG${N}"
        if [ $privapp == 1 ] && [ $sysapp == 1 ]; then
          echo " Where to install?"
          echo -n " < 1 - $SYSTEM/app | 2 - $SYSTEM/priv-app | 0 - cancel > : "
          read loc
          if [ "$loc" == 1 ]; then
            systemize /system/app
          elif [ "$loc" == 2 ]; then
            systemize /system/priv-app
          elif [ "$loc" == 0 ]; then
            echo " ${app[i]} canceled"; true
          else
            abort "Invalid!"
          fi
        elif [ $sysapp == 1 ] && [ $privapp == 0 ]; then
          systemize /system/app
        elif [ $privapp == 1 ] && [ $sysapp == 0 ]; then
          systemize /system/priv-app
        else
          abort " - No installation directory!"
        fi
      else
        abort " - APK doesn't exist!"
      fi
    fi
  done
  echo "Reboot to apply changes"
  echo -n "Return to menu? < y | n >: "
  read mchoice
  [ "$mchoice" == "y" ] && menu || exit
}

revert_apps() {
  list_systemized_apps 2>>$LOG
  clear
  echo "$div"
  title_div "Revert"
  echo "$div"
  echo ""
  for i in $(seq 1 $((${#rev_app[@]}-1))); do
    echo " $i - ${W}${rev_app[i]}${N}" | tr '_' ' '
  done
  echo ""
  echo " ${Bl}To systemize multiple apps, enter multiple number. Example:${N} \"1 ${#rev_app[$((${#rev_app[@]}/3))]} ${#rev_app[$((${#rev_app[@]}/2))]}\""
  echo " x - ${Bl}Back to Menu${N}"
  echo " 0 -  Exit"
  echo -n "[CHOICE]: "
  read revert
  [ "$revert" == "x" ] && menu
  for i in $revert; do
    if [ "$i" -gt ${#rev_app[@]} ] || [ "$i" -lt 0 ] || [ "$i" == "" ] || [ -n "$(echo $i | tr -d '0-9')" ] ; then
      abort "Invalid!"
    elif [ "$i" == 0 ]; then
      exit
    else
      echo "Reverting ${rev_app[i]}..." | tr '_' ' '
      rm -rf ${rev_dir[i]}
      rm -rf $MODDIR$SYSTEM2/etc/permissions/privapp-permissions-${rev_label[i]}.xml >>$LOG 2>&1
    fi
  done
  echo "Reboot to apply changes"
  echo -n "Return to menu? < y | n >: "
  read mchoice
  [ "$mchoice" == "y" ] && menu || exit
}

set_selinux() {
  clear
  echo "$div"
  title_div "Set SELinux"
  echo "$div"
  echo " This setting turns on the ability to dynamically disable"
  echo " SELinux if enabled (only when needed to enable unrestricted"
  echo " actions) for the script to execute noticeably faster."
  echo " It is fairly safe to do since it will immediately return"
  echo " SELinux status after a certain action."
  echo "$div"
  echo ""
  stat_color=$G
  $faster_flag || se_set="$(grep_prop selinux $MODDIR/module.prop)"
  [ "$se_set" != true ] && se_set=false && stat_color=$R
  echo " SELinux Status: $(getenforce)"
  echo " Enabled: ${stat_color}$se_set${N}"
  [ $(getenforce) == "Permissive" ] && echo " ${Y}SELinux is disabled! No need to enable this if it's permissive.${N}"
  echo ""
  echo " c - ${W}Change status${N}"
  echo " x - ${Bl}Back to Menu${N}"
  echo " 0 - Exit"
  echo -n "[CHOICE]: "
  read sechoice
  case "$sechoice" in
    x|X) menu;;
    c|C) if $faster_flag; then
        echo "Using '-f' (faster) option, changes will not be applied!"
		exit
      elif $se_set; then
        set_file_prop selinux false $MODDIR/module.prop
        echo "Set to false"
        echo "Exiting script to apply changes"
        exit
      else
        set_file_prop selinux true $MODDIR/module.prop
        echo "Set to true"
        echo "Exiting script to apply changes"
        exit
      fi;;
    0) exit;;
    *) echo "Invalid!"; exit;;
  esac
}

menu() {
  mod_head
  stat_color=$G
  $faster_flag || se_set="$(grep_prop selinux $MODDIR/module.prop)"
  [ "$se_set" != true ] && se_set=false && stat_color=$R
  echo "Set SELinux Enabled: ${stat_color}$se_set${N}"
  echo "SELinux Status: $(getenforce)"
  echo "$div"
  echo ""
  echo " 1 - Systemize Installed Apps (Listed)"
  echo " 2 - Systemize Installed Apps (Enter label) "
  echo " 3 - Systemize given APK"
  if [[ $(find $MODDIR -name "*.apk" 2>>$LOG) ]]; then
    echo " 4 - Revert Systemized Apps"
  fi
  echo " s - Set SELinux"
  prandom " d - ${C}Donate${N}"
  echo " 0 - Exit"
  echo ""
  echo -n "[CHOICE]: "
  read choice
  case "$choice" in
    1) menu_list_systemized_apps;;
    2) enter_apk_name; exit $?;;
    3) enter_apk_dir; exit $?;;
    4) revert_apps; exit $?;;

    d|D) am start https://paypal.me/veez21 >>$LOG 2>&1;;
    logs) upload_logs;;
    s|S) set_selinux;;
    0) exit;;
    *) echo "Invalid!"; exit 1;;
  esac
}

case "$1" in
  -a) shift
    for i in "$@"; do
          [ $API -lt 30 ] && app_path="/data/app/${i}-*/base.apk"
          [ $API -ge 30 ] && app_path="/data/app/*/${i}-*/base.apk"
	  [ -d $SYSTEM/priv-app ] && systemize /system/priv-app "$app_path" || systemize /system/app "$app_path" "$i"
	  echo "$div"
	done
	exit;;
  -d) shift
    for i in "$@"; do
	  [ -d $SYSTEM/priv-app ] && systemize /system/priv-app "$i" || systemize /system/app "$i"
	  echo "$div"
	done
	exit;;
  -l) list_systemized_apps 2>>$LOG
    echo
    for i in ${!rev_dir[@]}; do
      title_div "App $i"
      echo "Name\t: $(echo ${rev_app[i]}| tr '_' ' ')\nLabel\t: ${rev_label[i]}\nDirectory\t: $(echo ${rev_dir[i]} | sed 's/.*\/system\///g')" | expand -t 10
    done
    exit;;
  -h|--help) help_me;;
esac

menu
exit $?
