android support! thanks to TachiWeb devs.

This commit is contained in:
Aria Moradi
2021-01-02 04:57:20 +03:30
parent ced07d4e1e
commit 1e46a0c78c
291 changed files with 68699 additions and 16 deletions

View File

@@ -0,0 +1,93 @@
package xyz.nulldev.androidcompat.pm
import android.content.pm.PackageInfo
import android.content.pm.Signature
import android.os.Bundle
import com.android.apksig.ApkVerifier
import com.googlecode.d2j.dex.Dex2jar
import net.dongliu.apk.parser.ApkFile
import net.dongliu.apk.parser.ApkParsers
import org.w3c.dom.Element
import org.w3c.dom.Node
import org.w3c.dom.NodeList
import java.io.File
import javax.imageio.ImageIO
import javax.xml.parsers.DocumentBuilderFactory
data class InstalledPackage(val root: File) {
val apk = File(root, "package.apk")
val jar = File(root, "translated.jar")
val icon = File(root, "icon.png")
val info: PackageInfo
get() = ApkParsers.getMetaInfo(apk).toPackageInfo(root, apk).also {
val parsed = ApkFile(apk)
val dbFactory = DocumentBuilderFactory.newInstance()
val dBuilder = dbFactory.newDocumentBuilder()
val doc = parsed.manifestXml.byteInputStream().use {
dBuilder.parse(it)
}
it.applicationInfo.metaData = Bundle().apply {
val appTag = doc.getElementsByTagName("application").item(0)
appTag?.childNodes?.toList()?.filter {
it.nodeType == Node.ELEMENT_NODE
}?.map {
it as Element
}?.filter {
it.tagName == "meta-data"
}?.map {
putString(it.attributes.getNamedItem("android:name").nodeValue,
it.attributes.getNamedItem("android:value").nodeValue)
}
}
it.signatures = (parsed.apkSingers.flatMap { it.certificateMetas }
/*+ parsed.apkV2Singers.flatMap { it.certificateMetas }*/) // Blocked by: https://github.com/hsiafan/apk-parser/issues/72
.map { Signature(it.data) }.toTypedArray()
}
fun verify(): Boolean {
val res = ApkVerifier.Builder(apk)
.build()
.verify()
return res.isVerified
}
fun writeIcon() {
try {
val icons = ApkFile(apk).allIcons
val read = icons.filter { it.isFile }.map {
it.data.inputStream().use {
ImageIO.read(it)
}
}.sortedByDescending { it.width * it.height }.firstOrNull() ?: return
ImageIO.write(read, "png", icon)
} catch(e: Exception) {
icon.delete()
}
}
fun writeJar() {
try {
Dex2jar.from(apk).to(jar.toPath())
} catch(e: Exception) {
jar.delete()
}
}
private fun NodeList.toList(): List<Node> {
val out = mutableListOf<Node>()
for(i in 0 until length)
out += item(i)
return out
}
}

View File

@@ -0,0 +1,87 @@
package xyz.nulldev.androidcompat.pm
import net.dongliu.apk.parser.ApkParsers
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import xyz.nulldev.androidcompat.io.AndroidFiles
import java.io.File
class PackageController {
private val androidFiles by DI.global.instance<AndroidFiles>()
private val uninstallListeners = mutableListOf<(String) -> Unit>()
fun registerUninstallListener(listener: (String) -> Unit) {
uninstallListeners.add(listener)
}
fun unregisterUninstallListener(listener: (String) -> Unit) {
uninstallListeners.remove(listener)
}
private fun findRoot(apk: File): File {
val pn = ApkParsers.getMetaInfo(apk).packageName
return File(androidFiles.packagesDir, pn)
}
fun installPackage(apk: File, allowReinstall: Boolean) {
val root = findRoot(apk)
if (root.exists()) {
if (!allowReinstall) {
throw IllegalStateException("Package already installed!")
} else {
// TODO Compare past and new signature
root.deleteRecursively()
}
}
try {
root.mkdirs()
val installed = InstalledPackage(root)
apk.copyTo(installed.apk)
installed.writeIcon()
installed.writeJar()
if (!installed.jar.exists()) {
throw IllegalStateException("Failed to translate APK dex!")
}
} catch(t: Throwable) {
root.deleteRecursively()
throw t
}
}
fun listInstalled(): List<InstalledPackage> {
return androidFiles.packagesDir.listFiles().orEmpty().filter {
it.isDirectory
}.map {
InstalledPackage(it)
}
}
fun deletePackage(pack: InstalledPackage) {
if(!pack.root.exists()) error("Package was never installed!")
val packageName = pack.info.packageName
pack.root.deleteRecursively()
uninstallListeners.forEach {
it(packageName)
}
}
fun findPackage(packageName: String): InstalledPackage? {
val file = File(androidFiles.packagesDir, packageName)
return if(file.exists())
InstalledPackage(file)
else
null
}
fun findJarFromApk(apkFile: File): File? {
val pkgName = ApkParsers.getMetaInfo(apkFile).packageName
return findPackage(pkgName)?.jar
}
}

View File

@@ -0,0 +1,27 @@
package xyz.nulldev.androidcompat.pm
import android.content.pm.ApplicationInfo
import android.content.pm.FeatureInfo
import android.content.pm.PackageInfo
import net.dongliu.apk.parser.bean.ApkMeta
import java.io.File
fun ApkMeta.toPackageInfo(root: File, apk: File): PackageInfo {
return PackageInfo().also {
it.packageName = packageName
it.versionCode = versionCode.toInt()
it.versionName = versionName
it.reqFeatures = usesFeatures.map {
FeatureInfo().apply {
name = it.name
}
}.toTypedArray()
it.applicationInfo = ApplicationInfo().apply {
packageName = it.packageName
nonLocalizedLabel = label
sourceDir = apk.absolutePath
}
}
}