Fully Implement Silent Installation

Automated Code Formatting
This commit is contained in:
LooKeR
2021-10-13 00:32:34 +05:30
parent 8c8b8509a7
commit a8336bbde0
8 changed files with 432 additions and 378 deletions

View File

@ -1,15 +1,31 @@
package com.looker.droidify.utility
import android.animation.ValueAnimator
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.Signature
import android.graphics.drawable.Drawable
import android.net.Uri
import android.provider.Settings
import android.util.Log
import com.looker.droidify.R
import com.looker.droidify.content.Cache
import com.looker.droidify.content.Preferences
import com.looker.droidify.entity.InstalledItem
import com.looker.droidify.entity.Product
import com.looker.droidify.entity.Repository
import com.looker.droidify.service.Connection
import com.looker.droidify.service.DownloadService
import com.looker.droidify.utility.extension.android.Android
import com.looker.droidify.utility.extension.android.singleSignature
import com.looker.droidify.utility.extension.android.versionCodeCompat
import com.looker.droidify.utility.extension.resources.getColorFromAttr
import com.looker.droidify.utility.extension.resources.getDrawableCompat
import com.looker.droidify.utility.extension.text.hex
import com.topjohnwu.superuser.Shell
import java.io.File
import java.security.MessageDigest
import java.security.cert.Certificate
import java.security.cert.CertificateEncodingException
@ -21,6 +37,11 @@ object Utils {
.apply { setTintList(context.getColorFromAttr(tintAttrResId)) }
}
fun PackageInfo.toInstalledItem(): InstalledItem {
val signatureString = singleSignature?.let(Utils::calculateHash).orEmpty()
return InstalledItem(packageName, versionName.orEmpty(), versionCodeCompat, signatureString)
}
fun getDefaultApplicationIcons(context: Context): Pair<Drawable, Drawable> {
val progressIcon: Drawable =
createDefaultApplicationIcon(context, android.R.attr.textColorSecondary)
@ -78,4 +99,92 @@ object Utils {
) != 0f
}
}
internal fun Activity.startPackageInstaller(cacheFileName: String) {
val file = Cache.getReleaseFile(this, cacheFileName)
if (Preferences[Preferences.Key.RootPermission]) {
val commandBuilder = StringBuilder()
val verifyState = getVerifyState()
if (verifyState == "1") commandBuilder.append("settings put global verifier_verify_adb_installs 0 ; ")
commandBuilder.append(getPackageInstallCommand(file))
commandBuilder.append(" ; settings put global verifier_verify_adb_installs $verifyState")
val result = Shell.su(commandBuilder.toString()).exec()
if (result.isSuccess) Shell.su("${getUtilBoxPath()} rm ${quote(file.absolutePath)}")
} else {
val (uri, flags) = if (Android.sdk(24)) {
Pair(
Cache.getReleaseUri(this, cacheFileName),
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
} else {
Pair(Uri.fromFile(file), 0)
}
// TODO Handle deprecation
@Suppress("DEPRECATION")
startActivity(
Intent(Intent.ACTION_INSTALL_PACKAGE)
.setDataAndType(uri, "application/vnd.android.package-archive").setFlags(flags)
)
}
}
fun startUpdate(
packageName: String,
installedItem: InstalledItem?,
products: List<Pair<Product, Repository>>,
downloadConnection: Connection<DownloadService.Binder, DownloadService>
) {
val productRepository = Product.findSuggested(products, installedItem) { it.first }
val compatibleReleases = productRepository?.first?.selectedReleases.orEmpty()
.filter { installedItem == null || installedItem.signature == it.signature }
val release = if (compatibleReleases.size >= 2) {
compatibleReleases
.filter { it.platforms.contains(Android.primaryPlatform) }
.minByOrNull { it.platforms.size }
?: compatibleReleases.minByOrNull { it.platforms.size }
?: compatibleReleases.firstOrNull()
} else {
compatibleReleases.firstOrNull()
}
val binder = downloadConnection.binder
if (productRepository != null && release != null && binder != null) {
binder.enqueue(
packageName,
productRepository.first.name,
productRepository.second,
release
)
} else Unit
}
private fun getPackageInstallCommand(cacheFile: File): String =
"cat \"${cacheFile.absolutePath}\" | pm install -t -r -S ${cacheFile.length()}"
private fun getVerifyState(): String =
Shell.sh("settings get global verifier_verify_adb_installs").exec().out[0]
private fun quote(string: String) =
"\"${string.replace(Regex("""[\\$"`]""")) { c -> "\\${c.value}" }}\""
private fun getUtilBoxPath(): String {
listOf("toybox", "busybox").forEach {
var shellResult = Shell.su("which $it").exec()
if (shellResult.out.isNotEmpty()) {
val utilBoxPath = shellResult.out.joinToString("")
if (utilBoxPath.isNotEmpty()) {
val utilBoxQuoted = quote(utilBoxPath)
shellResult = Shell.su("$utilBoxQuoted --version").exec()
if (shellResult.out.isNotEmpty()) {
val utilBoxVersion = shellResult.out.joinToString("")
Log.i(
this.javaClass.canonicalName,
"Using Utilbox $it : $utilBoxQuoted $utilBoxVersion"
)
}
return utilBoxQuoted
}
}
}
return ""
}
}