mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-12-25 09:07:50 +00:00
Migrate to Activity Result APIs
This commit is contained in:
parent
98deec232b
commit
1bdd6e1a9d
@ -1,8 +1,10 @@
|
|||||||
package com.topjohnwu.magisk.arch
|
package com.topjohnwu.magisk.arch
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.databinding.ViewDataBinding
|
import androidx.databinding.ViewDataBinding
|
||||||
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
|
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
|
||||||
@ -23,12 +25,13 @@ import java.util.concurrent.CountDownLatch
|
|||||||
abstract class BaseMainActivity<VM : BaseViewModel, Binding : ViewDataBinding>
|
abstract class BaseMainActivity<VM : BaseViewModel, Binding : ViewDataBinding>
|
||||||
: BaseUIActivity<VM, Binding>() {
|
: BaseUIActivity<VM, Binding>() {
|
||||||
|
|
||||||
private val latch = CountDownLatch(1)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var doPreload = true
|
private var doPreload = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val latch = CountDownLatch(1)
|
||||||
|
private val uninstallPkg = registerForActivityResult(UninstallPackage) { latch.countDown() }
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setTheme(Theme.selected.themeRes)
|
setTheme(Theme.selected.themeRes)
|
||||||
|
|
||||||
@ -107,19 +110,24 @@ abstract class BaseMainActivity<VM : BaseViewModel, Binding : ViewDataBinding>
|
|||||||
if (Config.suManager.isNotEmpty())
|
if (Config.suManager.isNotEmpty())
|
||||||
Config.suManager = ""
|
Config.suManager = ""
|
||||||
pkg ?: return
|
pkg ?: return
|
||||||
if (!Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec().isSuccess)
|
if (!Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec().isSuccess) {
|
||||||
uninstallApp(pkg)
|
uninstallPkg.launch(pkg)
|
||||||
}
|
// Wait for the uninstallation to finish
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
private fun uninstallApp(pkg: String) {
|
|
||||||
val uri = Uri.Builder().scheme("package").opaquePart(pkg).build()
|
|
||||||
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, uri)
|
|
||||||
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
|
|
||||||
startActivityForResult(intent) { _, _ ->
|
|
||||||
latch.countDown()
|
|
||||||
}
|
|
||||||
latch.await()
|
latch.await()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object UninstallPackage : ActivityResultContract<String, Boolean>() {
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
override fun createIntent(context: Context, input: String): Intent {
|
||||||
|
val uri = Uri.Builder().scheme("package").opaquePart(input).build()
|
||||||
|
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, uri)
|
||||||
|
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
|
||||||
|
return intent
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseResult(resultCode: Int, intent: Intent?) = resultCode == RESULT_OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -77,7 +77,7 @@ abstract class BaseViewModel(
|
|||||||
PermissionEvent(permission, callback).publish()
|
PermissionEvent(permission, callback).publish()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withExternalRW(callback: () -> Unit) {
|
inline fun withExternalRW(crossinline callback: () -> Unit) {
|
||||||
withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) {
|
withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) {
|
||||||
if (!it) {
|
if (!it) {
|
||||||
SnackbarEvent(R.string.external_rw_permission_denied).publish()
|
SnackbarEvent(R.string.external_rw_permission_denied).publish()
|
||||||
|
@ -1,39 +1,32 @@
|
|||||||
package com.topjohnwu.magisk.core.base
|
package com.topjohnwu.magisk.core.base
|
||||||
|
|
||||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.Toast
|
import androidx.activity.result.contract.ActivityResultContracts.GetContent
|
||||||
import androidx.annotation.CallSuper
|
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.collection.SparseArrayCompat
|
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import com.topjohnwu.magisk.R
|
|
||||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||||
import com.topjohnwu.magisk.core.wrap
|
import com.topjohnwu.magisk.core.wrap
|
||||||
import com.topjohnwu.magisk.ktx.reflectField
|
import com.topjohnwu.magisk.ktx.reflectField
|
||||||
import com.topjohnwu.magisk.ktx.set
|
|
||||||
import com.topjohnwu.magisk.utils.Utils
|
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
typealias ActivityResultCallback = BaseActivity.(Int, Intent?) -> Unit
|
|
||||||
|
|
||||||
abstract class BaseActivity : AppCompatActivity() {
|
abstract class BaseActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private val resultCallbacks by lazy { SparseArrayCompat<ActivityResultCallback>() }
|
private var permissionCallback: ((Boolean) -> Unit)? = null
|
||||||
private val newRequestCode: Int get() {
|
private val requestPermission = registerForActivityResult(RequestPermission()) {
|
||||||
var requestCode: Int
|
permissionCallback?.invoke(it)
|
||||||
do {
|
permissionCallback = null
|
||||||
requestCode = Random.nextInt(0, 1 shl 15)
|
}
|
||||||
} while (resultCallbacks.containsKey(requestCode))
|
|
||||||
return requestCode
|
private var contentCallback: ((Uri) -> Unit)? = null
|
||||||
|
private val getContent = registerForActivityResult(GetContent()) {
|
||||||
|
if (it != null) contentCallback?.invoke(it)
|
||||||
|
contentCallback = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyOverrideConfiguration(config: Configuration?) {
|
override fun applyOverrideConfiguration(config: Configuration?) {
|
||||||
@ -57,72 +50,23 @@ abstract class BaseActivity : AppCompatActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withPermission(permission: String, builder: PermissionRequestBuilder.() -> Unit) {
|
fun withPermission(permission: String, callback: (Boolean) -> Unit) {
|
||||||
val request = PermissionRequestBuilder().apply(builder).build()
|
|
||||||
|
|
||||||
if (permission == WRITE_EXTERNAL_STORAGE && Build.VERSION.SDK_INT >= 30) {
|
if (permission == WRITE_EXTERNAL_STORAGE && Build.VERSION.SDK_INT >= 30) {
|
||||||
// We do not need external rw on 30+
|
// We do not need external rw on 30+
|
||||||
request.onSuccess()
|
callback(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
permissionCallback = callback
|
||||||
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
|
requestPermission.launch(permission)
|
||||||
request.onSuccess()
|
|
||||||
} else {
|
|
||||||
val requestCode = newRequestCode
|
|
||||||
resultCallbacks[requestCode] = { result, _ ->
|
|
||||||
if (result > 0)
|
|
||||||
request.onSuccess()
|
|
||||||
else
|
|
||||||
request.onFailure()
|
|
||||||
}
|
|
||||||
ActivityCompat.requestPermissions(this, arrayOf(permission), requestCode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withExternalRW(builder: PermissionRequestBuilder.() -> Unit) {
|
fun getContent(type: String, callback: (Uri) -> Unit) {
|
||||||
withPermission(WRITE_EXTERNAL_STORAGE, builder = builder)
|
contentCallback = callback
|
||||||
}
|
getContent.launch(type)
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(
|
|
||||||
requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
||||||
var success = true
|
|
||||||
for (res in grantResults) {
|
|
||||||
if (res != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
success = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resultCallbacks[requestCode]?.also {
|
|
||||||
resultCallbacks.remove(requestCode)
|
|
||||||
it(this, if (success) 1 else -1, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@CallSuper
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
|
||||||
resultCallbacks[requestCode]?.also { callback ->
|
|
||||||
resultCallbacks.remove(requestCode)
|
|
||||||
callback(this, resultCode, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun startActivityForResult(intent: Intent, callback: ActivityResultCallback) {
|
|
||||||
val requestCode = newRequestCode
|
|
||||||
resultCallbacks[requestCode] = callback
|
|
||||||
try {
|
|
||||||
startActivityForResult(intent, requestCode)
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
Utils.toast(R.string.app_not_found, Toast.LENGTH_SHORT)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun recreate() {
|
override fun recreate() {
|
||||||
startActivity(Intent().setComponent(intent.component))
|
startActivity(Intent().setComponent(intent.component))
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.core.base
|
|
||||||
|
|
||||||
typealias SimpleCallback = () -> Unit
|
|
||||||
typealias PermissionRationaleCallback = (List<String>) -> Unit
|
|
||||||
|
|
||||||
class PermissionRequestBuilder {
|
|
||||||
|
|
||||||
private var onSuccessCallback: SimpleCallback = {}
|
|
||||||
private var onFailureCallback: SimpleCallback = {}
|
|
||||||
private var onShowRationaleCallback: PermissionRationaleCallback = {}
|
|
||||||
|
|
||||||
fun onSuccess(callback: SimpleCallback) {
|
|
||||||
onSuccessCallback = callback
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onFailure(callback: SimpleCallback) {
|
|
||||||
onFailureCallback = callback
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onShowRationale(callback: PermissionRationaleCallback) {
|
|
||||||
onShowRationaleCallback = callback
|
|
||||||
}
|
|
||||||
|
|
||||||
fun build(): PermissionRequest {
|
|
||||||
return PermissionRequest(onSuccessCallback, onFailureCallback, onShowRationaleCallback)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class PermissionRequest(
|
|
||||||
private val onSuccessCallback: SimpleCallback,
|
|
||||||
private val onFailureCallback: SimpleCallback,
|
|
||||||
private val onShowRationaleCallback: PermissionRationaleCallback
|
|
||||||
) {
|
|
||||||
|
|
||||||
fun onSuccess() = onSuccessCallback()
|
|
||||||
fun onFailure() = onFailureCallback()
|
|
||||||
fun onShowRationale(permissions: List<String>) = onShowRationaleCallback(permissions)
|
|
||||||
|
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
package com.topjohnwu.magisk.events
|
package com.topjohnwu.magisk.events
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.net.Uri
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.navigation.NavDirections
|
import androidx.navigation.NavDirections
|
||||||
@ -11,18 +10,12 @@ import com.topjohnwu.magisk.MainDirections
|
|||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.arch.*
|
import com.topjohnwu.magisk.arch.*
|
||||||
import com.topjohnwu.magisk.core.Const
|
import com.topjohnwu.magisk.core.Const
|
||||||
import com.topjohnwu.magisk.core.base.ActivityResultCallback
|
|
||||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
|
||||||
import com.topjohnwu.magisk.core.model.module.OnlineModule
|
import com.topjohnwu.magisk.core.model.module.OnlineModule
|
||||||
import com.topjohnwu.magisk.events.dialog.MarkDownDialog
|
import com.topjohnwu.magisk.events.dialog.MarkDownDialog
|
||||||
import com.topjohnwu.magisk.utils.Utils
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
import com.topjohnwu.magisk.view.MagiskDialog
|
import com.topjohnwu.magisk.view.MagiskDialog
|
||||||
import com.topjohnwu.magisk.view.Shortcuts
|
import com.topjohnwu.magisk.view.Shortcuts
|
||||||
|
|
||||||
class ViewActionEvent(val action: BaseActivity.() -> Unit) : ViewEvent(), ActivityExecutor {
|
|
||||||
override fun invoke(activity: BaseUIActivity<*, *>) = action(activity)
|
|
||||||
}
|
|
||||||
|
|
||||||
class OpenReadmeEvent(private val item: OnlineModule) : MarkDownDialog() {
|
class OpenReadmeEvent(private val item: OnlineModule) : MarkDownDialog() {
|
||||||
override suspend fun getMarkdownText() = item.notes()
|
override suspend fun getMarkdownText() = item.notes()
|
||||||
override fun build(dialog: MagiskDialog) {
|
override fun build(dialog: MagiskDialog) {
|
||||||
@ -39,14 +32,7 @@ class PermissionEvent(
|
|||||||
) : ViewEvent(), ActivityExecutor {
|
) : ViewEvent(), ActivityExecutor {
|
||||||
|
|
||||||
override fun invoke(activity: BaseUIActivity<*, *>) =
|
override fun invoke(activity: BaseUIActivity<*, *>) =
|
||||||
activity.withPermission(permission) {
|
activity.withPermission(permission, callback)
|
||||||
onSuccess {
|
|
||||||
callback(true)
|
|
||||||
}
|
|
||||||
onFailure {
|
|
||||||
callback(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class BackPressEvent : ViewEvent(), ActivityExecutor {
|
class BackPressEvent : ViewEvent(), ActivityExecutor {
|
||||||
@ -75,12 +61,12 @@ class RecreateEvent : ViewEvent(), ActivityExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MagiskInstallFileEvent(private val callback: ActivityResultCallback)
|
class MagiskInstallFileEvent(
|
||||||
: ViewEvent(), ActivityExecutor {
|
private val callback: (Uri) -> Unit
|
||||||
|
) : ViewEvent(), ActivityExecutor {
|
||||||
override fun invoke(activity: BaseUIActivity<*, *>) {
|
override fun invoke(activity: BaseUIActivity<*, *>) {
|
||||||
val intent = Intent(Intent.ACTION_GET_CONTENT).setType("*/*")
|
|
||||||
try {
|
try {
|
||||||
activity.startActivityForResult(intent, callback)
|
activity.getContent("*/*", callback)
|
||||||
Utils.toast(R.string.patch_file_msg, Toast.LENGTH_LONG)
|
Utils.toast(R.string.patch_file_msg, Toast.LENGTH_LONG)
|
||||||
} catch (e: ActivityNotFoundException) {
|
} catch (e: ActivityNotFoundException) {
|
||||||
Utils.toast(R.string.app_not_found, Toast.LENGTH_SHORT)
|
Utils.toast(R.string.app_not_found, Toast.LENGTH_SHORT)
|
||||||
@ -106,17 +92,12 @@ class AddHomeIconEvent : ViewEvent(), ContextExecutor {
|
|||||||
|
|
||||||
class SelectModuleEvent : ViewEvent(), FragmentExecutor {
|
class SelectModuleEvent : ViewEvent(), FragmentExecutor {
|
||||||
override fun invoke(fragment: BaseUIFragment<*, *>) {
|
override fun invoke(fragment: BaseUIFragment<*, *>) {
|
||||||
val intent = Intent(Intent.ACTION_GET_CONTENT).setType("application/zip")
|
|
||||||
try {
|
try {
|
||||||
fragment.apply {
|
fragment.apply {
|
||||||
activity.startActivityForResult(intent) { code, intent ->
|
activity.getContent("application/zip") {
|
||||||
if (code == Activity.RESULT_OK && intent != null) {
|
|
||||||
intent.data?.also {
|
|
||||||
MainDirections.actionFlashFragment(Const.Value.FLASH_ZIP, it).navigate()
|
MainDirections.actionFlashFragment(Const.Value.FLASH_ZIP, it).navigate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
} catch (e: ActivityNotFoundException) {
|
||||||
Utils.toast(R.string.app_not_found, Toast.LENGTH_SHORT)
|
Utils.toast(R.string.app_not_found, Toast.LENGTH_SHORT)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.topjohnwu.magisk.ui.install
|
package com.topjohnwu.magisk.ui.install
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.databinding.Bindable
|
import androidx.databinding.Bindable
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
@ -42,10 +41,7 @@ class InstallViewModel(
|
|||||||
set(value) = set(value, _method, { _method = it }, BR.method) {
|
set(value) = set(value, _method, { _method = it }, BR.method) {
|
||||||
when (it) {
|
when (it) {
|
||||||
R.id.method_patch -> {
|
R.id.method_patch -> {
|
||||||
MagiskInstallFileEvent { code, intent ->
|
MagiskInstallFileEvent { uri -> data = uri }.publish()
|
||||||
if (code == Activity.RESULT_OK)
|
|
||||||
data = intent?.data
|
|
||||||
}.publish()
|
|
||||||
}
|
}
|
||||||
R.id.method_inactive_slot -> {
|
R.id.method_inactive_slot -> {
|
||||||
SecondSlotWarningDialog().publish()
|
SecondSlotWarningDialog().publish()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user