添加版本下载代码

This commit is contained in:
林若思 2026-01-04 16:06:20 +08:00
parent 201a60cf65
commit 36e2b55b07
2 changed files with 423 additions and 0 deletions

View File

@ -20,6 +20,7 @@ import androidx.annotation.RequiresPermission
import androidx.core.app.ActivityCompat
import androidx.lifecycle.lifecycleScope
import com.blankj.utilcode.constant.PermissionConstants
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.GsonUtils
import com.blankj.utilcode.util.LogUtils
@ -46,6 +47,7 @@ import com.zs.smarthuman.sherpa.VoiceState
import com.zs.smarthuman.toast.Toaster
import com.zs.smarthuman.utils.AudioDebugUtil
import com.zs.smarthuman.utils.AudioPcmUtil
import com.zs.smarthuman.utils.DangerousUtils
import com.zs.smarthuman.utils.UnityPlayerHolder
import com.zs.smarthuman.utils.ViewSlideAnimator
@ -55,10 +57,12 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import rxhttp.awaitResult
import rxhttp.toAwaitString
import rxhttp.toDownloadFlow
import rxhttp.wrapper.param.RxHttp
import rxhttp.wrapper.param.toAwaitResponse
import java.io.File
@ -449,6 +453,42 @@ class MainActivity : BaseViewModelActivity<ActivityMainBinding, MainViewModel>()
}
}
/**
* 下载并且安装
*/
private suspend fun downloadApk(apkUrl: String) {
val destPath =
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.path + "/" + "smartHuman.apk"
RxHttp.get(apkUrl)
.toDownloadFlow(destPath)
.onProgress {
lifecycleScope.launch(Dispatchers.Main) {
}
}.catch {
lifecycleScope.launch(Dispatchers.Main) {
}
}.collect {
//下载完成
if (AppUtils.isAppRoot()) {
lifecycleScope.launch(Dispatchers.Main) {
DangerousUtils.installAppSilent(it, "-r")
}
} else {
lifecycleScope.launch(Dispatchers.Main) {
AppUtils.installApp(it)
}
}
}
}
override fun onDestroy() {
super.onDestroy()

View File

@ -0,0 +1,383 @@
package com.zs.smarthuman.utils
import android.Manifest.permission
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.PowerManager
import android.telephony.SmsManager
import android.telephony.TelephonyManager
import android.text.TextUtils
import android.util.Log
import androidx.annotation.RequiresPermission
import com.blankj.utilcode.util.IntentUtils
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.ShellUtils
import com.blankj.utilcode.util.Utils
import java.io.File
import kotlin.jvm.javaClass
import kotlin.jvm.javaPrimitiveType
import kotlin.text.contains
import kotlin.text.toLowerCase
/**
* <pre>
* author: Blankj
* blog : http://blankj.com
* time : 2019/06/29
* desc :
</pre> *
*/
class DangerousUtils private constructor() {
companion object {
///////////////////////////////////////////////////////////////////////////
// AppUtils
///////////////////////////////////////////////////////////////////////////
/**
* Install the app silently.
*
* Without root permission must hold
* `android:sharedUserId="android.uid.shell"` and
* `<uses-permission android:name="android.permission.INSTALL_PACKAGES" />`
*
* @param filePath The path of file.
* @return `true`: success<br></br>`false`: fail
*/
fun installAppSilent(filePath: String): Boolean {
return installAppSilent(getFileByPath(filePath), null)
}
/**
* Install the app silently.
*
* Without root permission must hold
* `android:sharedUserId="android.uid.shell"` and
* `<uses-permission android:name="android.permission.INSTALL_PACKAGES" />`
*
* @param filePath The path of file.
* @param params The params of installation(e.g.,`-r`, `-s`).
* @return `true`: success<br></br>`false`: fail
*/
fun installAppSilent(filePath: String, params: String?): Boolean {
return installAppSilent(getFileByPath(filePath), params)
}
/**
* Install the app silently.
*
* Without root permission must hold
* `android:sharedUserId="android.uid.shell"` and
* `<uses-permission android:name="android.permission.INSTALL_PACKAGES" />`
*
* @param file The file.
* @param params The params of installation(e.g.,`-r`, `-s`).
* @param isRooted True to use root, false otherwise.
* @return `true`: success<br></br>`false`: fail
*/
/**
* Install the app silently.
*
* Without root permission must hold
* `android:sharedUserId="android.uid.shell"` and
* `<uses-permission android:name="android.permission.INSTALL_PACKAGES" />`
*
* @param file The file.
* @param params The params of installation(e.g.,`-r`, `-s`).
* @return `true`: success<br></br>`false`: fail
*/
/**
* Install the app silently.
*
* Without root permission must hold
* `android:sharedUserId="android.uid.shell"` and
* `<uses-permission android:name="android.permission.INSTALL_PACKAGES" />`
*
* @param file The file.
* @return `true`: success<br></br>`false`: fail
*/
@JvmOverloads
fun installAppSilent(
file: File?,
params: String? = null,
isRooted: Boolean = isDeviceRooted
): Boolean {
if (!isFileExists(file)) return false
val filePath = Char.toString() + file!!.absolutePath + '"'
val command = ("LD_LIBRARY_PATH=/vendor/lib*:/system/lib* pm install " +
(if (params == null) "" else "$params ")
+ filePath)
val commandResult = ShellUtils.execCmd(command, isRooted)
return if (commandResult.successMsg != null
&& commandResult.successMsg.toLowerCase().contains("success")
) {
true
} else {
Log.e(
"AppUtils", "installAppSilent successMsg: " + commandResult.successMsg +
", errorMsg: " + commandResult.errorMsg
)
false
}
}
/**
* Uninstall the app silently.
*
* Without root permission must hold
* `android:sharedUserId="android.uid.shell"` and
* `<uses-permission android:name="android.permission.DELETE_PACKAGES" />`
*
* @param packageName The name of the package.
* @param isKeepData Is keep the data.
* @param isRooted True to use root, false otherwise.
* @return `true`: success<br></br>`false`: fail
*/
/**
* Uninstall the app silently.
*
* Without root permission must hold
* `android:sharedUserId="android.uid.shell"` and
* `<uses-permission android:name="android.permission.DELETE_PACKAGES" />`
*
* @param packageName The name of the package.
* @param isKeepData Is keep the data.
* @return `true`: success<br></br>`false`: fail
*/
/**
* Uninstall the app silently.
*
* Without root permission must hold
* `android:sharedUserId="android.uid.shell"` and
* `<uses-permission android:name="android.permission.DELETE_PACKAGES" />`
*
* @param packageName The name of the package.
* @return `true`: success<br></br>`false`: fail
*/
@JvmOverloads
fun uninstallAppSilent(
packageName: String,
isKeepData: Boolean = false,
isRooted: Boolean = isDeviceRooted
): Boolean {
if (isSpace(packageName)) return false
val command = ("LD_LIBRARY_PATH=/vendor/lib*:/system/lib* pm uninstall "
+ (if (isKeepData) "-k " else "")
+ packageName)
val commandResult = ShellUtils.execCmd(command, isRooted)
return if (commandResult.successMsg != null
&& commandResult.successMsg.toLowerCase().contains("success")
) {
true
} else {
Log.e(
"AppUtils", "uninstallAppSilent successMsg: " + commandResult.successMsg +
", errorMsg: " + commandResult.errorMsg
)
false
}
}
private fun isFileExists(file: File?): Boolean {
return file != null && file.exists()
}
private fun getFileByPath(filePath: String): File? {
return if (isSpace(filePath)) null else File(filePath)
}
private fun isSpace(s: String?): Boolean {
if (s == null) return true
var i = 0
val len = s.length
while (i < len) {
if (!Character.isWhitespace(s[i])) {
return false
}
++i
}
return true
}
private val isDeviceRooted: Boolean
private get() {
val su = "su"
val locations = arrayOf(
"/system/bin/",
"/system/xbin/",
"/sbin/",
"/system/sd/xbin/",
"/system/bin/failsafe/",
"/data/local/xbin/",
"/data/local/bin/",
"/data/local/",
"/system/sbin/",
"/usr/bin/",
"/vendor/bin/"
)
for (location in locations) {
if (File(location + su).exists()) {
return true
}
}
return false
}
///////////////////////////////////////////////////////////////////////////
// DeviceUtils
///////////////////////////////////////////////////////////////////////////
/**
* Shutdown the device
*
* Requires root permission
* or hold `android:sharedUserId="android.uid.system"`,
* `<uses-permission android:name="android.permission.SHUTDOWN" />`
* in manifest.
*
* @return `true`: success<br></br>`false`: fail
*/
fun shutdown(): Boolean {
return try {
val result = ShellUtils.execCmd("reboot -p", true)
if (result.result == 0) return true
Utils.getApp()
.startActivity(IntentUtils.getShutdownIntent())
true
} catch (e: Exception) {
false
}
}
/**
* Reboot the device.
*
* Requires root permission
* or hold `android:sharedUserId="android.uid.system"` in manifest.
*
* @return `true`: success<br></br>`false`: fail
*/
fun reboot(): Boolean {
return try {
val result = ShellUtils.execCmd("reboot", true)
if (result.result == 0) return true
val intent = Intent(Intent.ACTION_REBOOT)
intent.putExtra("nowait", 1)
intent.putExtra("interval", 1)
intent.putExtra("window", 0)
Utils.getApp().sendBroadcast(intent)
true
} catch (e: Exception) {
false
}
}
/**
* Reboot the device.
*
* Requires root permission
* or hold `android:sharedUserId="android.uid.system"`,
* `<uses-permission android:name="android.permission.REBOOT" />`
*
* @param reason code to pass to the kernel (e.g., "recovery") to
* request special boot modes, or null.
* @return `true`: success<br></br>`false`: fail
*/
@SuppressLint("MissingPermission")
fun reboot(reason: String?): Boolean {
return try {
val pm = Utils.getApp()
.getSystemService(Context.POWER_SERVICE) as PowerManager
pm.reboot(reason)
true
} catch (e: Exception) {
false
}
}
/**
* Reboot the device to recovery.
*
* Requires root permission.
*
* @return `true`: success<br></br>`false`: fail
*/
fun reboot2Recovery(): Boolean {
val result = ShellUtils.execCmd("reboot recovery", true)
return result.result == 0
}
/**
* Reboot the device to bootloader.
*
* Requires root permission.
*
* @return `true`: success<br></br>`false`: fail
*/
fun reboot2Bootloader(): Boolean {
val result = ShellUtils.execCmd("reboot bootloader", true)
return result.result == 0
}
/**
* Enable or disable mobile data.
*
* Must hold `android:sharedUserId="android.uid.system"`,
* `<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />`
*
* @param enabled True to enabled, false otherwise.
* @return `true`: success<br></br>`false`: fail
*/
@RequiresPermission(permission.MODIFY_PHONE_STATE)
fun setMobileDataEnabled(enabled: Boolean): Boolean {
try {
val tm = Utils.getApp()
.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
tm.isDataEnabled = enabled
return true
}
val setDataEnabledMethod = tm.javaClass.getDeclaredMethod(
"setDataEnabled",
Boolean::class.javaPrimitiveType
)
setDataEnabledMethod.invoke(tm, enabled)
return true
} catch (e: Exception) {
Log.e("NetworkUtils", "setMobileDataEnabled: ", e)
}
return false
}
/**
* Send sms silently.
*
* Must hold `<uses-permission android:name="android.permission.SEND_SMS" />`
*
* @param phoneNumber The phone number.
* @param content The content.
*/
@SuppressLint("UnspecifiedImmutableFlag")
@RequiresPermission(permission.SEND_SMS)
fun sendSmsSilent(phoneNumber: String?, content: String) {
if (TextUtils.isEmpty(content)) return
val sentIntent = PendingIntent.getBroadcast(
Utils.getApp(),
0,
Intent("send"),
0
)
val smsManager = SmsManager.getDefault()
if (content.length >= 70) {
val ms: List<String> = smsManager.divideMessage(content)
for (str in ms) {
smsManager.sendTextMessage(phoneNumber, null, str, sentIntent, null)
}
} else {
smsManager.sendTextMessage(phoneNumber, null, content, sentIntent, null)
}
}
}
init {
throw kotlin.UnsupportedOperationException("u can't instantiate me...")
}
}