Cleanup ActivityResult callbacks

This commit is contained in:
topjohnwu 2020-10-06 02:04:19 -07:00
parent 333fe6da0e
commit fc19b50290
11 changed files with 58 additions and 106 deletions

View File

@ -1,6 +1,5 @@
package com.topjohnwu.magisk.arch package com.topjohnwu.magisk.arch
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.KeyEvent import android.view.KeyEvent
import android.view.View import android.view.View
@ -41,11 +40,6 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
AppCompatDelegate.setDefaultNightMode(theme) AppCompatDelegate.setDefaultNightMode(theme)
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
currentFragment?.onActivityResult(requestCode, resultCode, data)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setTheme(themeRes) setTheme(themeRes)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View File

@ -17,7 +17,7 @@ import com.topjohnwu.magisk.ktx.startAnimations
abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> : abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
Fragment(), BaseUIComponent<VM> { Fragment(), BaseUIComponent<VM> {
protected val activity get() = requireActivity() as BaseUIActivity<*, *> val activity get() = requireActivity() as BaseUIActivity<*, *>
protected lateinit var binding: Binding protected lateinit var binding: Binding
protected abstract val layoutRes: Int protected abstract val layoutRes: Int

View File

@ -1,7 +1,6 @@
package com.topjohnwu.magisk.arch package com.topjohnwu.magisk.arch
import android.content.Context import android.content.Context
import androidx.fragment.app.Fragment
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
/** /**
@ -23,5 +22,5 @@ interface ActivityExecutor {
} }
interface FragmentExecutor { interface FragmentExecutor {
operator fun invoke(fragment: Fragment) operator fun invoke(fragment: BaseUIFragment<*, *>)
} }

View File

@ -33,12 +33,12 @@ object Const {
object ID { object ID {
const val FETCH_ZIP = 2 const val FETCH_ZIP = 2
const val SELECT_BOOT = 3 const val SELECT_FILE = 3
const val MAX_ACTIVITY_RESULT = 10
// notifications // notifications
const val MAGISK_UPDATE_NOTIFICATION_ID = 4 const val MAGISK_UPDATE_NOTIFICATION_ID = 4
const val APK_UPDATE_NOTIFICATION_ID = 5 const val APK_UPDATE_NOTIFICATION_ID = 5
const val DTBO_NOTIFICATION_ID = 7
const val HIDE_MANAGER_NOTIFICATION_ID = 8 const val HIDE_MANAGER_NOTIFICATION_ID = 8
const val UPDATE_NOTIFICATION_CHANNEL = "update" const val UPDATE_NOTIFICATION_CHANNEL = "update"
const val PROGRESS_NOTIFICATION_CHANNEL = "progress" const val PROGRESS_NOTIFICATION_CHANNEL = "progress"

View File

@ -8,22 +8,24 @@ import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build import android.os.Build
import android.widget.Toast import android.widget.Toast
import androidx.annotation.CallSuper
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.collection.SparseArrayCompat import androidx.collection.SparseArrayCompat
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Const
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.set import com.topjohnwu.magisk.ktx.set
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
import kotlin.random.Random import kotlin.random.Random
typealias RequestCallback = BaseActivity.(Int, Intent?) -> Unit typealias ActivityResultCallback = BaseActivity.(Int, Intent?) -> Unit
abstract class BaseActivity : AppCompatActivity() { abstract class BaseActivity : AppCompatActivity() {
private val resultCallbacks by lazy { SparseArrayCompat<RequestCallback>() } private val resultCallbacks by lazy { SparseArrayCompat<ActivityResultCallback>() }
override fun applyOverrideConfiguration(config: Configuration?) { override fun applyOverrideConfiguration(config: Configuration?) {
// Force applying our preferred local // Force applying our preferred local
@ -38,8 +40,7 @@ abstract class BaseActivity : AppCompatActivity() {
fun withPermission(permission: String, builder: PermissionRequestBuilder.() -> Unit) { fun withPermission(permission: String, builder: PermissionRequestBuilder.() -> Unit) {
val request = PermissionRequestBuilder().apply(builder).build() val request = PermissionRequestBuilder().apply(builder).build()
if (permission == Manifest.permission.WRITE_EXTERNAL_STORAGE && if (permission == Manifest.permission.WRITE_EXTERNAL_STORAGE && Build.VERSION.SDK_INT >= 29) {
Build.VERSION.SDK_INT >= 29) {
// We do not need external rw on 29+ // We do not need external rw on 29+
request.onSuccess() request.onSuccess()
return return
@ -48,8 +49,11 @@ abstract class BaseActivity : AppCompatActivity() {
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
request.onSuccess() request.onSuccess()
} else { } else {
val requestCode = Random.nextInt(256, 512) var requestCode: Int
resultCallbacks[requestCode] = { result, _ -> do {
requestCode = Random.nextInt(Const.ID.MAX_ACTIVITY_RESULT + 1, 1 shl 15)
} while (!resultCallbacks.containsKey(requestCode))
resultCallbacks[requestCode] = { result, _ ->
if (result > 0) if (result > 0)
request.onSuccess() request.onSuccess()
else else
@ -79,16 +83,17 @@ abstract class BaseActivity : AppCompatActivity() {
} }
@CallSuper
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
resultCallbacks[requestCode]?.also { resultCallbacks[requestCode]?.also { callback ->
resultCallbacks.remove(requestCode) resultCallbacks.remove(requestCode)
it(this, resultCode, data) callback(this, resultCode, data)
} }
} }
fun startActivityForResult(intent: Intent, requestCode: Int, listener: RequestCallback) { fun startActivityForResult(intent: Intent, requestCode: Int, callback: ActivityResultCallback) {
resultCallbacks[requestCode] = listener resultCallbacks[requestCode] = callback
try { try {
startActivityForResult(intent, requestCode) startActivityForResult(intent, requestCode)
} catch (e: ActivityNotFoundException) { } catch (e: ActivityNotFoundException) {

View File

@ -1,45 +0,0 @@
package com.topjohnwu.magisk.events
import android.Manifest
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.widget.Toast
import androidx.annotation.RequiresPermission
import androidx.navigation.NavDirections
import com.topjohnwu.magisk.MainDirections
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.ActivityExecutor
import com.topjohnwu.magisk.arch.BaseUIActivity
import com.topjohnwu.magisk.arch.ViewEvent
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.utils.Utils
class InstallExternalModuleEvent : ViewEvent(), ActivityExecutor {
@RequiresPermission(allOf = [Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE])
override fun invoke(activity: BaseUIActivity<*, *>) {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "application/zip"
try {
activity.startActivityForResult(intent, Const.ID.FETCH_ZIP)
} catch (e: ActivityNotFoundException) {
Utils.toast(R.string.app_not_found, Toast.LENGTH_SHORT)
}
}
companion object {
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): NavDirections? {
if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
val data = data.data
if (data != null) {
return MainDirections.actionFlashFragment(data, Const.Value.FLASH_ZIP)
}
}
return null
}
}
}

View File

@ -6,8 +6,11 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.widget.Toast import android.widget.Toast
import androidx.navigation.NavDirections import androidx.navigation.NavDirections
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.base.ActivityResultCallback
import com.topjohnwu.magisk.core.base.BaseActivity import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.model.module.Repo import com.topjohnwu.magisk.core.model.module.Repo
import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.utils.Utils
@ -67,27 +70,16 @@ class RecreateEvent : ViewEvent(), ActivityExecutor {
} }
} }
class RequestFileEvent : ViewEvent(), ActivityExecutor { class MagiskInstallFileEvent(private val callback: ActivityResultCallback)
: ViewEvent(), ActivityExecutor {
override fun invoke(activity: BaseUIActivity<*, *>) { override fun invoke(activity: BaseUIActivity<*, *>) {
Intent(Intent.ACTION_GET_CONTENT) val intent = Intent(Intent.ACTION_GET_CONTENT).setType("*/*")
.setType("*/*") try {
.addCategory(Intent.CATEGORY_OPENABLE) activity.startActivityForResult(intent, Const.ID.SELECT_FILE, callback)
.also { Utils.toast(R.string.patch_file_msg, Toast.LENGTH_LONG)
try { } catch (e: ActivityNotFoundException) {
activity.startActivityForResult(it, REQUEST_CODE) Utils.toast(R.string.app_not_found, Toast.LENGTH_SHORT)
Utils.toast(R.string.patch_file_msg, Toast.LENGTH_LONG) }
} catch (e: ActivityNotFoundException) {
Utils.toast(R.string.app_not_found, Toast.LENGTH_SHORT)
}
}
}
companion object {
private const val REQUEST_CODE = 10
fun resolve(requestCode: Int, resultCode: Int, data: Intent?) = data
?.takeIf { resultCode == Activity.RESULT_OK }
?.takeIf { requestCode == REQUEST_CODE }
?.data
} }
} }
@ -106,3 +98,22 @@ class AddHomeIconEvent : ViewEvent(), ContextExecutor {
Shortcuts.addHomeIcon(context) Shortcuts.addHomeIcon(context)
} }
} }
class SelectModuleEvent : ViewEvent(), FragmentExecutor {
override fun invoke(fragment: BaseUIFragment<*, *>) {
val intent = Intent(Intent.ACTION_GET_CONTENT).setType("application/zip")
try {
fragment.apply {
activity.startActivityForResult(intent, Const.ID.FETCH_ZIP) { code, intent ->
if (code == Activity.RESULT_OK && intent != null) {
intent.data?.also {
MainDirections.actionFlashFragment(it, Const.Value.FLASH_ZIP).navigate()
}
}
}
}
} catch (e: ActivityNotFoundException) {
Utils.toast(R.string.app_not_found, Toast.LENGTH_SHORT)
}
}
}

View File

@ -1,12 +1,10 @@
package com.topjohnwu.magisk.ui.install package com.topjohnwu.magisk.ui.install
import android.content.Intent
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseUIFragment import com.topjohnwu.magisk.arch.BaseUIFragment
import com.topjohnwu.magisk.core.download.BaseDownloader import com.topjohnwu.magisk.core.download.BaseDownloader
import com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding import com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding
import com.topjohnwu.magisk.events.RequestFileEvent
import com.topjohnwu.magisk.ktx.coroutineScope import com.topjohnwu.magisk.ktx.coroutineScope
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
@ -15,11 +13,6 @@ class InstallFragment : BaseUIFragment<InstallViewModel, FragmentInstallMd2Bindi
override val layoutRes = R.layout.fragment_install_md2 override val layoutRes = R.layout.fragment_install_md2
override val viewModel by viewModel<InstallViewModel>() override val viewModel by viewModel<InstallViewModel>()
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
viewModel.data = RequestFileEvent.resolve(requestCode, resultCode, data)
}
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
requireActivity().setTitle(R.string.install) requireActivity().setTitle(R.string.install)

View File

@ -1,5 +1,6 @@
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
@ -11,7 +12,7 @@ import com.topjohnwu.magisk.core.download.Action
import com.topjohnwu.magisk.core.download.DownloadService import com.topjohnwu.magisk.core.download.DownloadService
import com.topjohnwu.magisk.core.download.Subject import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.data.repository.StringRepository import com.topjohnwu.magisk.data.repository.StringRepository
import com.topjohnwu.magisk.events.RequestFileEvent import com.topjohnwu.magisk.events.MagiskInstallFileEvent
import com.topjohnwu.magisk.events.dialog.SecondSlotWarningDialog import com.topjohnwu.magisk.events.dialog.SecondSlotWarningDialog
import com.topjohnwu.magisk.utils.set import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
@ -35,7 +36,10 @@ class InstallViewModel(
set(value) = set(value, field, { field = it }, BR.method) { set(value) = set(value, field, { field = it }, BR.method) {
when (it) { when (it) {
R.id.method_patch -> { R.id.method_patch -> {
RequestFileEvent().publish() MagiskInstallFileEvent { code, intent ->
if (code == Activity.RESULT_OK)
data = intent?.data
}.publish()
} }
R.id.method_inactive_slot -> { R.id.method_inactive_slot -> {
SecondSlotWarningDialog().publish() SecondSlotWarningDialog().publish()

View File

@ -1,6 +1,5 @@
package com.topjohnwu.magisk.ui.module package com.topjohnwu.magisk.ui.module
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
@ -14,7 +13,6 @@ import com.topjohnwu.magisk.arch.ReselectionTarget
import com.topjohnwu.magisk.arch.ViewEvent import com.topjohnwu.magisk.arch.ViewEvent
import com.topjohnwu.magisk.core.download.BaseDownloader import com.topjohnwu.magisk.core.download.BaseDownloader
import com.topjohnwu.magisk.databinding.FragmentModuleMd2Binding import com.topjohnwu.magisk.databinding.FragmentModuleMd2Binding
import com.topjohnwu.magisk.events.InstallExternalModuleEvent
import com.topjohnwu.magisk.ktx.hideKeyboard import com.topjohnwu.magisk.ktx.hideKeyboard
import com.topjohnwu.magisk.ui.MainActivity import com.topjohnwu.magisk.ui.MainActivity
import com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener import com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener
@ -40,13 +38,6 @@ class ModuleFragment : BaseUIFragment<ModuleViewModel, FragmentModuleMd2Binding>
} }
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
InstallExternalModuleEvent.onActivityResult(requestCode, resultCode, data)?.also {
it.navigate()
}
}
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
setHasOptionsMenu(true) setHasOptionsMenu(true)

View File

@ -14,7 +14,7 @@ import com.topjohnwu.magisk.core.tasks.RepoUpdater
import com.topjohnwu.magisk.data.database.RepoByNameDao import com.topjohnwu.magisk.data.database.RepoByNameDao
import com.topjohnwu.magisk.data.database.RepoByUpdatedDao import com.topjohnwu.magisk.data.database.RepoByUpdatedDao
import com.topjohnwu.magisk.databinding.RvItem import com.topjohnwu.magisk.databinding.RvItem
import com.topjohnwu.magisk.events.InstallExternalModuleEvent import com.topjohnwu.magisk.events.SelectModuleEvent
import com.topjohnwu.magisk.events.OpenChangelogEvent import com.topjohnwu.magisk.events.OpenChangelogEvent
import com.topjohnwu.magisk.events.SnackbarEvent import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.magisk.events.dialog.ModuleInstallDialog import com.topjohnwu.magisk.events.dialog.ModuleInstallDialog
@ -311,7 +311,7 @@ class ModuleViewModel(
} }
fun installPressed() = withExternalRW { fun installPressed() = withExternalRW {
InstallExternalModuleEvent().publish() SelectModuleEvent().publish()
} }
fun infoPressed(item: RepoItem) = fun infoPressed(item: RepoItem) =