diff --git a/app/src/main/java/com/zs/smarthuman/ui/MainActivity.kt b/app/src/main/java/com/zs/smarthuman/ui/MainActivity.kt index 7a2e5a6..2b0f8d4 100644 --- a/app/src/main/java/com/zs/smarthuman/ui/MainActivity.kt +++ b/app/src/main/java/com/zs/smarthuman/ui/MainActivity.kt @@ -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() } } + /** + * 下载并且安装 + */ + 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() diff --git a/app/src/main/java/com/zs/smarthuman/utils/DangeroutUtils.kt b/app/src/main/java/com/zs/smarthuman/utils/DangeroutUtils.kt new file mode 100644 index 0000000..2a69f21 --- /dev/null +++ b/app/src/main/java/com/zs/smarthuman/utils/DangeroutUtils.kt @@ -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 + + +/** + *
+ * author: Blankj
+ * blog  : http://blankj.com
+ * time  : 2019/06/29
+ * desc  :
+
* + */ +class DangerousUtils private constructor() { + companion object { + /////////////////////////////////////////////////////////////////////////// + // AppUtils + /////////////////////////////////////////////////////////////////////////// + /** + * Install the app silently. + * + * Without root permission must hold + * `android:sharedUserId="android.uid.shell"` and + * `` + * + * @param filePath The path of file. + * @return `true`: success

`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 + * `` + * + * @param filePath The path of file. + * @param params The params of installation(e.g.,`-r`, `-s`). + * @return `true`: success

`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 + * `` + * + * @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

`false`: fail + */ + /** + * Install the app silently. + * + * Without root permission must hold + * `android:sharedUserId="android.uid.shell"` and + * `` + * + * @param file The file. + * @param params The params of installation(e.g.,`-r`, `-s`). + * @return `true`: success

`false`: fail + */ + /** + * Install the app silently. + * + * Without root permission must hold + * `android:sharedUserId="android.uid.shell"` and + * `` + * + * @param file The file. + * @return `true`: success

`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 + * `` + * + * @param packageName The name of the package. + * @param isKeepData Is keep the data. + * @param isRooted True to use root, false otherwise. + * @return `true`: success

`false`: fail + */ + /** + * Uninstall the app silently. + * + * Without root permission must hold + * `android:sharedUserId="android.uid.shell"` and + * `` + * + * @param packageName The name of the package. + * @param isKeepData Is keep the data. + * @return `true`: success

`false`: fail + */ + /** + * Uninstall the app silently. + * + * Without root permission must hold + * `android:sharedUserId="android.uid.shell"` and + * `` + * + * @param packageName The name of the package. + * @return `true`: success

`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"`, + * `` + * in manifest. + * + * @return `true`: success

`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

`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"`, + * `` + * + * @param reason code to pass to the kernel (e.g., "recovery") to + * request special boot modes, or null. + * @return `true`: success

`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

`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

`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"`, + * `` + * + * @param enabled True to enabled, false otherwise. + * @return `true`: success

`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 `` + * + * @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 = 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...") + } +} \ No newline at end of file