diff --git a/.gitmodules b/.gitmodules
index 46f629d..1eff271 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,3 +10,6 @@
[submodule "app/src/c/libressl"]
path = dmg2img/src/c/libressl
url = https://github.com/libressl-portable/portable.git
+[submodule "termux-packages/src/c/termux-packages"]
+ path = termux-packages/src/c/termux-packages
+ url = https://github.com/Depau/termux-packages.git
diff --git a/app/build.gradle b/app/build.gradle
index ad7466d..0debf7b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,7 +8,7 @@ android {
compileSdkVersion 28
defaultConfig {
applicationId "eu.depau.etchdroid"
- minSdkVersion 19
+ minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
@@ -37,7 +37,7 @@ dependencies {
// implementation 'com.github.mjdev:libaums:0.5.5'
implementation project(':libaums')
implementation project(':dmg2img')
-
+ implementation project(':termux-packages')
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
diff --git a/build.gradle b/build.gradle
index 2ac0e46..b0f22a0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
+ ext.kotlin_version = '1.2.61'
ext.kotlin_version = '1.2.50'
repositories {
google()
diff --git a/settings.gradle b/settings.gradle
index 77a9903..8f5cfc8 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,2 @@
-include ':app', ':libaums', ':dmg2img'
+include ':app', ':libaums', ':dmg2img', ':termux-packages'
project(':libaums').projectDir = new File('libaums/libaums')
\ No newline at end of file
diff --git a/termux-packages/CMakeLists.txt b/termux-packages/CMakeLists.txt
new file mode 100644
index 0000000..d8c36ed
--- /dev/null
+++ b/termux-packages/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.4)
+
+set(ASSETS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/main/assets/termux/${ANDROID_ABI}")
+
+add_subdirectory(src/c/termux-wrapper)
+
+# Dummy target for Gradle to pick up termux_wrapper
+add_executable(termux_packages
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/c/dummy.c)
+add_dependencies(termux_packages termux_build)
\ No newline at end of file
diff --git a/termux-packages/build.gradle b/termux-packages/build.gradle
new file mode 100644
index 0000000..2390fb3
--- /dev/null
+++ b/termux-packages/build.gradle
@@ -0,0 +1,33 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ compileSdkVersion 28
+ defaultConfig {
+ minSdkVersion 21
+ externalNativeBuild {
+ cmake {
+ targets "termux_packages"
+ arguments "-DAPP_PKGNAME=eu.depau.etchdroid", "-DTERMUX_PACKAGES=parted"
+ }
+ }
+ }
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ }
+ }
+ sourceSets {
+ main {
+ assets.srcDirs('src/main/assets')
+ }
+ }
+}
+repositories {
+ mavenCentral()
+}
+dependencies {
+ compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ compile "org.kamranzafar:jtar:2.3"
+ compile "commons-io:commons-io:2.6"
+}
\ No newline at end of file
diff --git a/termux-packages/src/c/dummy.c b/termux-packages/src/c/dummy.c
new file mode 100644
index 0000000..237c8ce
--- /dev/null
+++ b/termux-packages/src/c/dummy.c
@@ -0,0 +1 @@
+int main() {}
diff --git a/termux-packages/src/c/termux-packages b/termux-packages/src/c/termux-packages
new file mode 160000
index 0000000..e45ad0d
--- /dev/null
+++ b/termux-packages/src/c/termux-packages
@@ -0,0 +1 @@
+Subproject commit e45ad0d7e998fc707dbf4e0f578482b32218e418
diff --git a/termux-packages/src/c/termux-wrapper/CMakeLists.txt b/termux-packages/src/c/termux-wrapper/CMakeLists.txt
new file mode 100644
index 0000000..e5bcd5f
--- /dev/null
+++ b/termux-packages/src/c/termux-wrapper/CMakeLists.txt
@@ -0,0 +1,33 @@
+cmake_minimum_required(VERSION 3.4)
+
+# NDK is assumed to be placed inside SDK directory
+# Should be implemented better
+get_filename_component(ANDROID_SDK ${ANDROID_NDK} DIRECTORY)
+
+if(${ANDROID_ABI} STREQUAL "x86")
+ set(TERMUX_ARCH "i686")
+elseif(${ANDROID_ABI} STREQUAL "arm64-v8a")
+ set(TERMUX_ARCH "aarch64")
+elseif(${ANDROID_ABI} STREQUAL "armeabi-v7a")
+ set(TERMUX_ARCH "arm")
+else()
+ set(TERMUX_ARCH "${ANDROID_ABI}")
+endif()
+
+set(TERMUX_TOPDIR "${PROJECT_BINARY_DIR}/termux-topdir")
+set(TERMUX_DEBDIR "${Project_BINARY_DIR}/termux-debs")
+set(TERMUX_PREFIX "/data/data/${APP_PKGNAME}/files/termux/usr")
+set(TERMUX_ANDROID_HOME "/data/data/${APP_PKGNAME}/files/termux/home")
+
+add_custom_target(termux_build
+ ${CMAKE_COMMAND} -E env
+ ASSETS_PATH=${ASSETS_PATH}
+ NDK=${ANDROID_NDK}
+ ANDROID_HOME=${ANDROID_SDK}
+ TERMUX_TOPDIR=${TERMUX_TOPDIR}
+ TERMUX_DEBDIR=${TERMUX_DEBDIR}
+ TERMUX_PREFIX=${TERMUX_PREFIX}
+ TERMUX_ANDROID_HOME=${TERMUX_ANDROID_HOME}
+ TERMUX_ARCH=${TERMUX_ARCH}
+ ${CMAKE_CURRENT_LIST_DIR}/build-packages.sh ${TERMUX_PACKAGES}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
diff --git a/termux-packages/src/c/termux-wrapper/build-packages.sh b/termux-packages/src/c/termux-wrapper/build-packages.sh
new file mode 100755
index 0000000..6896c34
--- /dev/null
+++ b/termux-packages/src/c/termux-wrapper/build-packages.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+cd ../termux-packages
+
+for package in "$@"
+do
+ ./build-package.sh -a $TERMUX_ARCH $package
+done
+
+cd ../termux-wrapper
+
+./install-packages.sh $TERMUX_DEBDIR $ASSETS_PATH
\ No newline at end of file
diff --git a/termux-packages/src/c/termux-wrapper/install-packages.sh b/termux-packages/src/c/termux-wrapper/install-packages.sh
new file mode 100755
index 0000000..81f3b03
--- /dev/null
+++ b/termux-packages/src/c/termux-wrapper/install-packages.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+
+DEBDIR="$1"
+DESTDIR="$2"
+
+echo "Creating output tarball"
+
+cd "$DEBDIR"
+
+(
+ rm -f pkg_info.txt
+ rm -Rf root
+ rm -Rf tmp
+) > /dev/null 2>&1
+
+mkdir root
+
+# Extract packages
+ls | sort | grep \\.deb | grep -v -- '-dev_' | while read pkg; do
+ mkdir tmp
+ cd tmp
+ ar -x ../$pkg
+ tar -xJf control.tar.xz
+ cat control >> ../pkg_info.txt
+ echo >> ../pkg_info.txt
+ cd ../root
+ tar -xJf ../tmp/data.tar.xz
+ cd ..
+ rm -Rf tmp
+done
+
+mkdir -p $DESTDIR
+
+# Create info files
+sha256sum pkg_info.txt > pkg_fprint.txt
+cp pkg_info.txt pkg_fprint.txt root/data/data/*/files/termux
+cp pkg_info.txt pkg_fprint.txt $DESTDIR
+
+# Cleanup unneeded files
+pushd root/data/data/*/files/termux/usr > /dev/null
+rm -Rf include
+rm -Rf share/doc share/info share/man
+
+# Create output tar
+cd ../..
+tar -cf $DESTDIR/packages.tar termux
+
+# Cleanup
+popd > /dev/null
+rm -Rf root pkg_info.txt pkg_fprint.txt
\ No newline at end of file
diff --git a/termux-packages/src/main/AndroidManifest.xml b/termux-packages/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..7f2802d
--- /dev/null
+++ b/termux-packages/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/termux-packages/src/main/java/eu/depau/termux_wrapper/TermuxWrapper.kt b/termux-packages/src/main/java/eu/depau/termux_wrapper/TermuxWrapper.kt
new file mode 100644
index 0000000..7bfcc9c
--- /dev/null
+++ b/termux-packages/src/main/java/eu/depau/termux_wrapper/TermuxWrapper.kt
@@ -0,0 +1,131 @@
+package eu.depau.termux_wrapper
+
+import android.content.Context
+import android.os.Build
+import org.apache.commons.io.FileUtils
+import org.kamranzafar.jtar.TarEntry
+import org.kamranzafar.jtar.TarInputStream
+import java.io.*
+import java.nio.file.attribute.PosixFilePermission
+
+
+@Throws(IOException::class)
+fun InputStream.readString(): String {
+ val baos = ByteArrayOutputStream()
+ val buffer = ByteArray(1024)
+ var length = this.read(buffer)
+
+ while (length != -1) {
+ baos.write(buffer, 0, length)
+ length = this.read(buffer)
+ }
+ return baos.toString("UTF-8")
+}
+
+
+class TermuxWrapper(val context: Context) {
+ public var path: MutableList = mutableListOf(
+ "/usr/local/sbin",
+ "/usr/local/bin",
+ "/usr/sbin",
+ "/usr/bin",
+ "/sbin",
+ "/bin"
+ )
+
+ val arch: String
+ get() {
+ val abi = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ Build.SUPPORTED_ABIS[0]
+ else
+ Build.CPU_ABI
+
+ return when {
+ abi.contains("armeabi-v7a") -> "armeabi-v7a"
+ abi.contains("x86_64") -> "x86_64"
+ abi.contains("x86") -> "x86"
+ abi.contains("arm64-v8a") -> "arm64-v8a"
+ else -> null!!
+ }
+ }
+
+ val needsUpdate: Boolean
+ get() {
+ val assetManager = context.assets
+ val hashAssets = assetManager.open("termux/$arch/pkg_fprint.txt").readString()
+ val hashFilesFile = File("${context.filesDir}/termux/pkg_fprint.txt")
+
+ if (!hashFilesFile.exists())
+ return true
+
+ val hashFiles = FileInputStream(hashFilesFile).readString()
+
+ return hashAssets != hashFiles
+ }
+
+ private fun applyPerms(tarEntry: TarEntry, file: File) {
+ // u 2 g 1 o 0
+ // [rwx] [rwx] [rwx]
+
+ val mode = tarEntry.header.mode
+ val permSet: MutableSet = mutableSetOf()
+
+ // Get owner permissions. On Android API < 26 there's no easy way to set the others
+ val stepInt = (mode shr (6)) and 0xFFF8
+
+ // Iterate over permission bits
+ for (bit in listOf(1, 2, 4)) {
+ val bool = (stepInt and bit) > 0
+ when (bit) {
+ 1 -> file.setExecutable(bool, true)
+ 2 -> file.setWritable(bool, true)
+ 4 -> file.setReadable(bool, true)
+ else -> null!!
+ }
+ }
+ }
+
+ fun doUpdate() {
+ val oldTarDir = File("${context.filesDir}/termux")
+ if (oldTarDir.exists())
+ FileUtils.deleteDirectory(oldTarDir)
+
+ val destDir = context.filesDir!!
+
+ val assetManager = context.assets
+ val termuxTar = TarInputStream(assetManager.open("termux/$arch/packages.tar").buffered())
+ var tarEntry = termuxTar.nextEntry
+
+ termuxTar.use { tis ->
+ while (tarEntry != null) {
+ val data = ByteArray(2048)
+ val destFile = File("$destDir/${tarEntry.name}")
+ val fos = FileOutputStream(destFile)
+ val dest = BufferedOutputStream(fos)
+ var count = tis.read(data)
+
+ while (count != -1) {
+ dest.write(data, 0, count)
+ count = tis.read(data)
+ }
+
+ dest.close()
+ applyPerms(tarEntry, destFile)
+
+ tarEntry = tis.nextEntry
+ }
+ }
+ }
+
+ fun getFile(path: String) = File("${context.filesDir}/termux/$path")
+
+ fun which(cmd: String): File? {
+ for (p in path) {
+ val file = getFile("$p/$cmd")
+
+ if (file.exists() && file.isFile && file.canExecute())
+ return file
+ }
+ return null
+ }
+}
\ No newline at end of file