This commit is contained in:
Syer10
2026-07-12 22:57:38 -04:00
parent 4fdef7d17d
commit c18155cf06
5 changed files with 77 additions and 190 deletions

View File

@@ -102,6 +102,7 @@ object Extension {
jarSavePath
}
}
apkUrl != null -> {
installAPK {
val apkName = Uri.parse(apkUrl).lastPathSegment!!
@@ -112,7 +113,10 @@ object Extension {
apkSavePath
}
}
else -> throw NullPointerException("Could not find extension url for $pkgName")
else -> {
throw NullPointerException("Could not find extension url for $pkgName")
}
}
}
@@ -247,7 +251,7 @@ object Extension {
pkgName,
packageInfo.versionName,
packageInfo.versionCode,
contentWarning
contentWarning,
)
} catch (e: Throwable) {
// free up the file descriptor if exists
@@ -270,16 +274,19 @@ object Extension {
): String {
val jarFile = Path(fetcher())
val jarZip = ZipFile.builder()
.setPath(jarFile)
.get()
val jarZip =
ZipFile
.builder()
.setPath(jarFile)
.get()
return jarZip.use { jarZip ->
val manifest = jarZip.getInputStream(jarZip.getEntry("AndroidManifest.xml"))
.use {
AndroidManifestParser.parse(it)
}
val manifest =
jarZip
.getInputStream(jarZip.getEntry("AndroidManifest.xml"))
.use {
AndroidManifestParser.parse(it)
}
val pkgName = manifest.packageName
// check if we don't have the extension already installed
@@ -316,20 +323,23 @@ object Extension {
// throw Exception("This apk is not a signed with the official tachiyomi signature")
// }
fun List<AndroidManifestParser.MetaData>.getString(name: String): String? {
return this.find { it.name == name }?.value
}
fun List<AndroidManifestParser.MetaData>.getString(name: String): String? = this.find { it.name == name }?.value
var contentWarning = manifest.application!!.metaData.getString(METADATA_CONTENT_WARNING)
?.toIntOrNull()
var contentWarning =
manifest.application!!
.metaData
.getString(METADATA_CONTENT_WARNING)
?.toIntOrNull()
if (contentWarning == null) {
contentWarning = manifest.application.metaData.getString(METADATA_NSFW)
contentWarning = manifest.application.metaData
.getString(METADATA_NSFW)
?.toIntOrNull()
?: 0
}
val sourceClass =
manifest.application.metaData.getString(METADATA_SOURCE_CLASS)!!
manifest.application.metaData
.getString(METADATA_SOURCE_CLASS)!!
.trim()
val className =
@@ -364,7 +374,7 @@ object Extension {
pkgName,
manifest.versionName,
manifest.versionCode!!,
contentWarning
contentWarning,
)
} catch (e: Throwable) {
// free up the file descriptor if exists
@@ -373,7 +383,8 @@ object Extension {
try {
uninstallExtension(pkgName)
} catch (_: Exception) {}
} catch (_: Exception) {
}
throw e
}
}
@@ -409,7 +420,6 @@ object Extension {
else -> "all"
}
// update extension info
transaction {
if (ExtensionTable.selectAll().where { ExtensionTable.pkgName eq pkgName }.firstOrNull() == null) {

View File

@@ -22,21 +22,16 @@ object AndroidManifestParser {
data class AndroidManifest(
@XmlSerialName("package", "", "")
val packageName: String,
@XmlSerialName("versionCode", ANDROID_NS, "android")
val versionCode: Int? = null,
@XmlSerialName("versionName", ANDROID_NS, "android")
val versionName: String? = null,
@XmlElement(true)
@XmlSerialName("uses-sdk", "", "")
val usesSdk: UsesSdk? = null,
@XmlElement(true)
@XmlSerialName("uses-feature", "", "")
val usesFeatures: List<UsesFeature> = emptyList(),
@XmlElement(true)
@XmlSerialName("application", "", "")
val application: Application? = null,
@@ -47,7 +42,6 @@ object AndroidManifestParser {
data class UsesSdk(
@XmlSerialName("minSdkVersion", ANDROID_NS, "android")
val minSdkVersion: Int? = null,
@XmlSerialName("targetSdkVersion", ANDROID_NS, "android")
val targetSdkVersion: Int? = null,
)
@@ -64,20 +58,15 @@ object AndroidManifestParser {
data class Application(
@XmlSerialName("label", ANDROID_NS, "android")
val label: String? = null,
@XmlSerialName("icon", ANDROID_NS, "android")
val icon: String? = null,
@XmlSerialName("allowBackup", ANDROID_NS, "android")
val allowBackup: Boolean? = null,
@XmlSerialName("extractNativeLibs", ANDROID_NS, "android")
val extractNativeLibs: Boolean? = null,
@XmlElement(true)
@XmlSerialName("meta-data", "", "")
val metaData: List<MetaData> = emptyList(),
@XmlElement(true)
@XmlSerialName("activity", "", "")
val activities: List<Activity> = emptyList(),
@@ -88,10 +77,8 @@ object AndroidManifestParser {
data class MetaData(
@XmlSerialName("name", ANDROID_NS, "android")
val name: String,
@XmlSerialName("value", ANDROID_NS, "android")
val value: String? = null,
@XmlSerialName("resource", ANDROID_NS, "android")
val resource: String? = null,
)
@@ -101,13 +88,10 @@ object AndroidManifestParser {
data class Activity(
@XmlSerialName("name", ANDROID_NS, "android")
val name: String,
@XmlSerialName("exported", ANDROID_NS, "android")
val exported: Boolean? = null,
@XmlSerialName("theme", ANDROID_NS, "android")
val theme: String? = null,
@XmlElement(true)
@XmlSerialName("intent-filter", "", "")
val intentFilters: List<IntentFilter> = emptyList(),
@@ -119,11 +103,9 @@ object AndroidManifestParser {
@XmlElement(true)
@XmlSerialName("action", "", "")
val actions: List<Action> = emptyList(),
@XmlElement(true)
@XmlSerialName("category", "", "")
val categories: List<Category> = emptyList(),
@XmlElement(true)
@XmlSerialName("data", "", "")
val data: List<Data> = emptyList(),
@@ -148,24 +130,22 @@ object AndroidManifestParser {
data class Data(
@XmlSerialName("scheme", ANDROID_NS, "android")
val scheme: String? = null,
@XmlSerialName("host", ANDROID_NS, "android")
val host: String? = null,
@XmlSerialName("pathPattern", ANDROID_NS, "android")
val pathPattern: String? = null,
)
private val xml = XML {
autoPolymorphic = false
repairNamespaces = true
xmlDeclMode = XmlDeclMode.Minimal
defaultPolicy {
unknownChildHandler = UnknownChildHandler { _, _, _, _, _ -> emptyList() }
private val xml =
XML {
autoPolymorphic = false
repairNamespaces = true
xmlDeclMode = XmlDeclMode.Minimal
defaultPolicy {
unknownChildHandler = UnknownChildHandler { _, _, _, _, _ -> emptyList() }
}
}
}
@OptIn(ExperimentalXmlUtilApi::class)
fun parse(input: InputStream): AndroidManifest =
xml.decodeFromReader(AndroidManifest.serializer(), KtXmlReader(input))
fun parse(input: InputStream): AndroidManifest = xml.decodeFromReader(AndroidManifest.serializer(), KtXmlReader(input))
}

View File

@@ -94,8 +94,8 @@ object PackageTools {
}
/** A modified version of `xyz.nulldev.androidcompat.pm.InstalledPackage.info` */
fun getPackageInfo(apkFile: Path): PackageInfo {
return ApkParsers.getMetaInfo(apkFile.toFile()).toPackageInfo(apkFile.toFile()).apply {
fun getPackageInfo(apkFile: Path): PackageInfo =
ApkParsers.getMetaInfo(apkFile.toFile()).toPackageInfo(apkFile.toFile()).apply {
val parsed = ApkFile(apkFile.toFile())
val dbFactory = DocumentBuilderFactory.newInstance()
val dBuilder = dbFactory.newDocumentBuilder()
@@ -137,7 +137,6 @@ object PackageTools {
.map { Signature(it.data) }
.toTypedArray()
}
}
fun getSignatureHash(pkgInfo: PackageInfo): String? {
val signatures = pkgInfo.signatures

View File

@@ -18,19 +18,23 @@ object ResourceArscIconParser {
fun extractIcon(
jar: Path,
iconPath: Path
iconPath: Path,
) {
ZipFile.builder()
ZipFile
.builder()
.setPath(jar)
.get()
.use { zip ->
val packages = zip.getInputStream(zip.getEntry("resources.arsc"))
.use { ArscParser(it.readBytes()).parse() }
val packages =
zip
.getInputStream(zip.getEntry("resources.arsc"))
.use { ArscParser(it.readBytes()).parse() }
val icon = packages
.flatMap { it.iconCandidates() }
.maxByOrNull { it.density }
?: return
val icon =
packages
.flatMap { it.iconCandidates() }
.maxByOrNull { it.density }
?: return
val entry = zip.getEntry(icon.path) ?: return
@@ -42,19 +46,21 @@ object ResourceArscIconParser {
}
}
fun extractIcon(
zip: ZipFile,
): InputStream {
val packages = zip.getInputStream(zip.getEntry("resources.arsc"))
.use { ArscParser(it.readBytes()).parse() }
fun extractIcon(zip: ZipFile): InputStream {
val packages =
zip
.getInputStream(zip.getEntry("resources.arsc"))
.use { ArscParser(it.readBytes()).parse() }
val icon = packages
.flatMap { it.iconCandidates() }
.maxByOrNull { it.density }
?: throw NullPointerException("No valid icons")
val icon =
packages
.flatMap { it.iconCandidates() }
.maxByOrNull { it.density }
?: throw NullPointerException("No valid icons")
val entry = zip.getEntry(icon.path)
?: throw NullPointerException("Icon ${icon.path} missing")
val entry =
zip.getEntry(icon.path)
?: throw NullPointerException("Icon ${icon.path} missing")
return zip.getInputStream(entry)
}
@@ -76,19 +82,22 @@ object ResourceArscIconParser {
}
private fun Config.density(): Int =
ByteBuffer.wrap(id)
ByteBuffer
.wrap(id)
.order(ByteOrder.LITTLE_ENDIAN)
.getShort(14)
.toInt() and 0xffff
private val rasterExtensions = setOf(
"png",
"webp",
"jpg",
"jpeg",
)
private val rasterExtensions =
setOf(
"png",
"webp",
"jpg",
"jpeg",
)
private fun isRasterImage(path: String): Boolean =
path.substringAfterLast('.', "")
path
.substringAfterLast('.', "")
.lowercase() in rasterExtensions
}

View File

@@ -1,111 +0,0 @@
import org.apache.commons.compress.archivers.zip.ZipFile
import pxb.android.arsc.ArscDumper
import pxb.android.arsc.ArscParser
import pxb.android.axml.AxmlParser
import suwayomi.tachidesk.manga.impl.util.AndroidManifestParser
import suwayomi.tachidesk.manga.impl.util.ResourceArscIconParser
import kotlin.io.path.Path
import kotlin.io.path.absolutePathString
import kotlin.test.Test
class JarTest {
@Test
fun jarTest() {
println(Path("./").absolutePathString())
val jar = Path("./build/tachiyomi-all.ahottie-v1.4.3.jar")
val zip = ZipFile.builder()
.setPath(jar)
.get()
val manifest = zip.getInputStream(zip.getEntry("AndroidManifest.xml"))
.use {
AndroidManifestParser.parse(it)
}
//
// reader.parse()
// println(reader.resourceTable)
// val resourceTable = reader.resourceTable
// println(resourceTable.stringPool)
// //println(table.stringPool.get(1))
// // table::class.members.forEach {
// // if (it.name == "packageMap") {
// // it.isAccessible = true
// // val map = it.call(table) as Map<Short, ResourcePackage>
// // map.forEach { short, packagz ->
// // println(short)
// // println(packagz.name to packagz.id)
// // }
// // }
// // }
// val apkTranslator = ApkMetaTranslator(resourceTable, null)
// val xmlTranslator = XmlTranslator()
// val xmlStreamer = CompositeXmlStreamer(xmlTranslator, apkTranslator)
// val data = zip.getInputStream(zip.getEntry("AndroidManifest.xml"))
// .use {
// it.readBytes()
// }
//
//
// val buffer = ByteBuffer.wrap(data)
// val binaryXmlParser = BinaryXmlParser(buffer, resourceTable)
// //binaryXmlParser.locale = preferredLocale
// binaryXmlParser.xmlStreamer = xmlStreamer
// binaryXmlParser.parse()
// val manifestXml = xmlTranslator.xml
// println(manifestXml)
// val iconPaths = apkTranslator.iconPaths
// iconPaths.forEach {
// println(it.path)
// }
// val parser = zip.getInputStream(zip.getEntry("resources.arsc"))
// .use {
// ArscParser(it.readBytes())
// }
// val parsed = parser.parse()
// ArscDumper.dump(parsed)
//
// parsed.forEach {
// print("pkg.name: ")
// println(it.name)
// it.types.toList().forEachIndexed { t, (i, type) ->
// print("pkg.types.$t.type.name: ")
// println(type.name)
// print("pkg.types.$t.type.id: ")
// println(type.id)
// type.specs.forEachIndexed { y, spec ->
// print("pkg.types.$t.type.specs.$y.id: ")
// println(spec.id)
// print("pkg.types.$t.type.specs.$y.name: ")
// println(spec.name)
// print("pkg.types.$t.type.specs.$y.flags: ")
// println(spec.flags)
// }
// type.configs.forEachIndexed { y, config ->
// print("pkg.types.$t.type.configs.$y.id: ")
// println(config.id.toHexString(HexFormat.UpperCase))
// print("pkg.types.$t.type.configs.$y.entryCount: ")
// println(config.entryCount)
// config.resources.forEach {
// print("pkg.types.$t.type.configs.$y.resources.${it.key}.flag: ")
// println(it.value.flag)
// print("pkg.types.$t.type.configs.$y.resources.${it.key}.value: ")
// println(it.value.value)
// print("pkg.types.$t.type.configs.$y.resources.${it.key}.spec.id: ")
// println(it.value.spec.id)
// print("pkg.types.$t.type.configs.$y.resources.${it.key}.spec.name: ")
// println(it.value.spec.name)
// print("pkg.types.$t.type.configs.$y.resources.${it.key}.spec.flags: ")
// println(it.value.spec.flags)
// }
// }
// }
// }
// val fileTest = Path("./build/icon.png")
// ResourceArscIconParser.extractIcon(jar, fileTest)
println(manifest.packageName)
}
}