package ani.dantotsu.addons import android.content.Context import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.os.Build import androidx.core.content.pm.PackageInfoCompat import ani.dantotsu.addons.download.DownloadAddon import ani.dantotsu.addons.download.DownloadAddonApi import ani.dantotsu.addons.download.DownloadAddonManager import ani.dantotsu.addons.download.DownloadLoadResult import ani.dantotsu.addons.torrent.TorrentAddon import ani.dantotsu.addons.torrent.TorrentAddonApi import ani.dantotsu.addons.torrent.TorrentAddonManager import ani.dantotsu.addons.torrent.TorrentLoadResult import ani.dantotsu.media.AddonType import ani.dantotsu.util.Logger import dalvik.system.PathClassLoader import eu.kanade.tachiyomi.extension.util.ExtensionLoader import eu.kanade.tachiyomi.util.system.getApplicationIcon class AddonLoader { companion object { fun loadExtension( context: Context, packageName: String, className: String, type: AddonType ): LoadResult? { val pkgManager = context.packageManager val installedPkgs = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { pkgManager.getInstalledPackages(PackageManager.PackageInfoFlags.of(ExtensionLoader.PACKAGE_FLAGS.toLong())) } else { pkgManager.getInstalledPackages(ExtensionLoader.PACKAGE_FLAGS) } val extPkgs = installedPkgs.filter { isPackageAnExtension( packageName, it ) } if (extPkgs.isEmpty()) return null if (extPkgs.size > 1) throw IllegalStateException("Multiple extensions with the same package name found") val pkgName = extPkgs.first().packageName val pkgInfo = extPkgs.first() val appInfo = try { pkgManager.getApplicationInfo(pkgName, PackageManager.GET_META_DATA) } catch (error: PackageManager.NameNotFoundException) { // Unlikely, but the package may have been uninstalled at this point Logger.log(error) throw error } val extName = pkgManager.getApplicationLabel(appInfo).toString().substringAfter("Dantotsu: ") val versionName = pkgInfo.versionName val versionCode = PackageInfoCompat.getLongVersionCode(pkgInfo) if (versionName.isNullOrEmpty()) { Logger.log("Missing versionName for extension $extName") throw IllegalStateException("Missing versionName for extension $extName") } val classLoader = PathClassLoader(appInfo.sourceDir, appInfo.nativeLibraryDir, context.classLoader) val loadedClass = try { Class.forName(className, false, classLoader) } catch (e: ClassNotFoundException) { Logger.log("Extension load error: $extName ($className)") Logger.log(e) throw e } catch (e: NoClassDefFoundError) { Logger.log("Extension load error: $extName ($className)") Logger.log(e) throw e } catch (e: Exception) { Logger.log("Extension load error: $extName ($className)") Logger.log(e) throw e } val instance = loadedClass.getDeclaredConstructor().newInstance() return when (type) { AddonType.TORRENT -> { val extension = instance as? TorrentAddonApi ?: throw IllegalStateException("Extension is not a TorrentAddonApi") TorrentLoadResult.Success( TorrentAddon.Installed( name = extName, pkgName = pkgName, versionName = versionName, versionCode = versionCode, extension = extension, icon = context.getApplicationIcon(pkgName), ) ) } AddonType.DOWNLOAD -> { val extension = instance as? DownloadAddonApi ?: throw IllegalStateException("Extension is not a DownloadAddonApi") DownloadLoadResult.Success( DownloadAddon.Installed( name = extName, pkgName = pkgName, versionName = versionName, versionCode = versionCode, extension = extension, icon = context.getApplicationIcon(pkgName), ) ) } } } fun loadFromPkgName(context: Context, packageName: String, type: AddonType): LoadResult? { return when (type) { AddonType.TORRENT -> loadExtension( context, packageName, TorrentAddonManager.TORRENT_CLASS, type ) AddonType.DOWNLOAD -> loadExtension( context, packageName, DownloadAddonManager.DOWNLOAD_CLASS, type ) } } private fun isPackageAnExtension(type: String, pkgInfo: PackageInfo): Boolean { return pkgInfo.packageName.equals(type) } } }