diff --git a/app/build.gradle b/app/build.gradle index 918d70d..360b62c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,7 @@ plugins { } android { - compileSdkVersion 31 + compileSdk 33 buildToolsVersion "31.0.0" defaultConfig { diff --git a/app/src/main/java/ltd/nextalone/pkginstallerplus/HookEntry.java b/app/src/main/java/ltd/nextalone/pkginstallerplus/HookEntry.java index 5dd803e..3a6eaa3 100644 --- a/app/src/main/java/ltd/nextalone/pkginstallerplus/HookEntry.java +++ b/app/src/main/java/ltd/nextalone/pkginstallerplus/HookEntry.java @@ -5,6 +5,7 @@ import android.content.res.Resources; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; +import android.widget.ExpandableListAdapter; import java.io.File; import java.lang.reflect.Method; @@ -16,6 +17,7 @@ import ltd.nextalone.pkginstallerplus.sdk25.PackageInstallerActivityHook; import ltd.nextalone.pkginstallerplus.sdk30.PackageInstallerActivityHook30; import ltd.nextalone.pkginstallerplus.sdk31.PackageInstallerActivityHook31; +import ltd.nextalone.pkginstallerplus.sdk33.PackageInstallerActivityHook33; import static ltd.nextalone.pkginstallerplus.utils.LogUtilsKt.logDebug; import static ltd.nextalone.pkginstallerplus.utils.LogUtilsKt.logDetail; @@ -37,25 +39,34 @@ private static void initializeHookInternal(LoadPackageParam lpparam) { logDebug("Hooked"); try { lpClassLoader = lpparam.classLoader; - if (VERSION.SDK_INT >= VERSION_CODES.S) { - PackageInstallerActivityHook31.INSTANCE.initOnce(); + if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + PackageInstallerActivityHook33.INSTANCE.initOnce(); } else { throw new UnsupportedClassVersionError(); } } catch (Exception e) { try { lpClassLoader = lpparam.classLoader; - if (VERSION.SDK_INT >= VERSION_CODES.P) { - PackageInstallerActivityHook30.INSTANCE.initOnce(); + if (VERSION.SDK_INT >= VERSION_CODES.S) { + PackageInstallerActivityHook31.INSTANCE.initOnce(); } else { throw new UnsupportedClassVersionError(); } } catch (Exception e1) { try { - PackageInstallerActivityHook.INSTANCE.initOnce(); + lpClassLoader = lpparam.classLoader; + if (VERSION.SDK_INT >= VERSION_CODES.P) { + PackageInstallerActivityHook30.INSTANCE.initOnce(); + } else { + throw new UnsupportedClassVersionError(); + } } catch (Exception e2) { - e.addSuppressed(e2); - logThrowable("initializeHookInternal: ", e); + try { + PackageInstallerActivityHook.INSTANCE.initOnce(); + } catch (Exception e3) { + e.addSuppressed(e3); + logThrowable("initializeHookInternal: ", e); + } } } } diff --git a/app/src/main/java/ltd/nextalone/pkginstallerplus/sdk31/PackageInstallerActivityHook31.kt b/app/src/main/java/ltd/nextalone/pkginstallerplus/sdk31/PackageInstallerActivityHook31.kt index c267d47..4b45122 100644 --- a/app/src/main/java/ltd/nextalone/pkginstallerplus/sdk31/PackageInstallerActivityHook31.kt +++ b/app/src/main/java/ltd/nextalone/pkginstallerplus/sdk31/PackageInstallerActivityHook31.kt @@ -62,7 +62,11 @@ object PackageInstallerActivityHook31 { if (oldPkgInfo == null) { val install: View? = activity.findHostView("install_confirm_question") val oldVersionStr = (newPkgInfo.versionName ?: "N/A") + "(" + newPkgInfo.longVersionCode + ")" - sb.append("包名: $pkgName\n版本: $oldVersionStr") + sb.append("包名: ") + .append(pkgName, ForegroundColorSpan(ThemeUtil.colorGreen), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + .append('\n') + .append("版本: ") + .append(oldVersionStr, ForegroundColorSpan(ThemeUtil.colorGreen), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) if (install != null) { layout.setPadding(0, install.height, 0, 0) textView.text = sb @@ -73,7 +77,13 @@ object PackageInstallerActivityHook31 { val update: View? = activity.findHostView("install_confirm_question_update") val oldVersionStr = """${oldPkgInfo.versionName ?: "N/A"}(${oldPkgInfo.longVersionCode})""" val newVersionStr = """${newPkgInfo.versionName ?: "N/A"}(${newPkgInfo.longVersionCode})""" - sb.append("包名: $pkgName\n版本: $oldVersionStr ➞ $newVersionStr") + sb.append("包名: ") + .append(pkgName, ForegroundColorSpan(ThemeUtil.colorGreen), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + .append('\n') + .append("版本: ") + .append(oldVersionStr, ForegroundColorSpan(ThemeUtil.colorRed), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + .append(" ➞ ") + .append(newVersionStr, ForegroundColorSpan(ThemeUtil.colorGreen), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) if (update != null) { layout.setPadding(0, update.height, 0, 0) textView.text = sb diff --git a/app/src/main/java/ltd/nextalone/pkginstallerplus/sdk33/PackageInstallerActivityHook33.kt b/app/src/main/java/ltd/nextalone/pkginstallerplus/sdk33/PackageInstallerActivityHook33.kt new file mode 100644 index 0000000..d6e5d1d --- /dev/null +++ b/app/src/main/java/ltd/nextalone/pkginstallerplus/sdk33/PackageInstallerActivityHook33.kt @@ -0,0 +1,135 @@ +package ltd.nextalone.pkginstallerplus.sdk33 + +import android.app.Activity +import android.app.AlertDialog +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.graphics.Typeface +import android.text.SpannableStringBuilder +import android.text.Spanned +import android.text.style.ForegroundColorSpan +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import android.widget.TextView +import androidx.annotation.RequiresApi +import ltd.nextalone.pkginstallerplus.HookEntry.injectModuleResources +import ltd.nextalone.pkginstallerplus.dip2px +import ltd.nextalone.pkginstallerplus.utils.* + +@RequiresApi(33) +object PackageInstallerActivityHook33 { + fun initOnce() { + "com.android.packageinstaller.PackageInstallerActivity".clazz?.method("startInstallConfirm") + ?.hookAfter { + val ctx: Activity = it.thisObject as Activity + Thread { + Thread.sleep(100) + ctx.runOnUiThread { + addInstallDetails(ctx) + } + }.start() + } + + "com.android.packageinstaller.UninstallerActivity".clazz?.method("showConfirmationDialog") + ?.hookBefore { + val ctx: Activity = it.thisObject as Activity + injectModuleResources(ctx.resources) + "com.android.packageinstaller.handheld.UninstallAlertDialogFragment".clazz?.method("onCreateDialog") + ?.hookAfter { it2 -> + val dialog = it2.result as AlertDialog + addUninstallDetails(ctx, dialog) + } + } + } + + private fun addInstallDetails(activity: Activity) { + val textView = TextView(activity) + textView.setTextIsSelectable(true) + textView.typeface = Typeface.MONOSPACE + val layout = LinearLayout(activity) + val newPkgInfo: PackageInfo = activity.get("mPkgInfo") as PackageInfo + val pkgName = newPkgInfo.packageName + val oldPkgInfo = try { + activity.packageManager.getPackageInfo( + pkgName, + PackageManager.MATCH_UNINSTALLED_PACKAGES + ) + } catch (e: PackageManager.NameNotFoundException) { + null + } + val sb = SpannableStringBuilder() + if (oldPkgInfo == null) { + val install: View? = activity.findHostView("install_confirm_question") + val oldVersionStr = (newPkgInfo.versionName ?: "N/A") + "(" + newPkgInfo.longVersionCode + ")" + sb.append("包名: ") + .append(pkgName, ForegroundColorSpan(ThemeUtil.colorGreen), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + .append('\n') + .append("版本: ") + .append(oldVersionStr, ForegroundColorSpan(ThemeUtil.colorGreen), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + if (install != null) { + layout.setPadding(0, install.height, 0, 0) + textView.text = sb + layout.addView(textView, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)) + (install.parent as ViewGroup).addView(layout) + } + } else { + val update: View? = activity.findHostView("install_confirm_question_update") + val oldVersionStr = """${oldPkgInfo.versionName ?: "N/A"}(${oldPkgInfo.longVersionCode})""" + val newVersionStr = """${newPkgInfo.versionName ?: "N/A"}(${newPkgInfo.longVersionCode})""" + sb.append("包名: ") + .append(pkgName, ForegroundColorSpan(ThemeUtil.colorGreen), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + .append('\n') + .append("版本: ") + .append(oldVersionStr, ForegroundColorSpan(ThemeUtil.colorRed), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + .append(" ➞ ") + .append(newVersionStr, ForegroundColorSpan(ThemeUtil.colorGreen), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + + if (update != null) { + layout.setPadding(0, update.height, 0, 0) + textView.text = sb + layout.addView(textView, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)) + (update.parent as ViewGroup).addView(layout) + } + } + } + + private fun addUninstallDetails(activity: Activity, dialog: AlertDialog) { + val textView = TextView(activity) + textView.setTextIsSelectable(true) + textView.typeface = Typeface.MONOSPACE + val layout = LinearLayout(activity) + if (activity.taskId == -1) return + val packageName = activity.get("mDialogInfo")?.get("appInfo")?.get("packageName") as String + val oldPkgInfo = try { + activity.packageManager.getPackageInfo( + packageName, + PackageManager.MATCH_UNINSTALLED_PACKAGES + ) + } catch (e: PackageManager.NameNotFoundException) { + null + } + val sb = SpannableStringBuilder() + if (oldPkgInfo != null) { + val oldVersionStr = (oldPkgInfo.versionName ?: "N/A") + "(" + oldPkgInfo.longVersionCode + ")" + sb.append("包名: ") + .append( + packageName, + ForegroundColorSpan(ThemeUtil.colorRed), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + .append('\n') + .append("版本: ") + .append( + oldVersionStr, + ForegroundColorSpan(ThemeUtil.colorRed), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + layout.setPadding(activity.dip2px(24f), 0, activity.dip2px(24f), 0) + textView.text = sb + layout.addView(textView, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)) + dialog.setView(layout) + } + } +} +