mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-27 20:15:29 +00:00
Check REQUEST_INSTALL_PACKAGES before actions
This commit is contained in:
parent
e36596470c
commit
5d400fbe90
@ -1,23 +1,27 @@
|
||||
package com.topjohnwu.magisk.arch
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
import android.widget.Toast
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.*
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.JobService
|
||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||
import com.topjohnwu.magisk.core.tasks.HideAPK
|
||||
import com.topjohnwu.magisk.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.ui.theme.Theme
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.view.MagiskDialog
|
||||
import com.topjohnwu.magisk.view.Notifications
|
||||
import com.topjohnwu.magisk.view.Shortcuts
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
abstract class BaseMainActivity<Binding : ViewDataBinding> : NavigationActivity<Binding>() {
|
||||
|
||||
@ -25,9 +29,6 @@ abstract class BaseMainActivity<Binding : ViewDataBinding> : NavigationActivity<
|
||||
private var doPreload = true
|
||||
}
|
||||
|
||||
private val latch = CountDownLatch(1)
|
||||
private val uninstallPkg = registerForActivityResult(UninstallPackage) { latch.countDown() }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setTheme(Theme.selected.themeRes)
|
||||
|
||||
@ -67,18 +68,28 @@ abstract class BaseMainActivity<Binding : ViewDataBinding> : NavigationActivity<
|
||||
|
||||
abstract fun showMainUI(savedInstanceState: Bundle?)
|
||||
|
||||
private fun showInvalidStateMessage() {
|
||||
runOnUiThread {
|
||||
MagiskDialog(this).apply {
|
||||
setTitle(R.string.unsupport_nonroot_stub_title)
|
||||
setMessage(R.string.unsupport_nonroot_stub_msg)
|
||||
setButton(MagiskDialog.ButtonType.POSITIVE) {
|
||||
text = R.string.install
|
||||
onClick { HideAPK.restore(this@BaseMainActivity) }
|
||||
@SuppressLint("InlinedApi")
|
||||
private fun showInvalidStateMessage(): Unit = runOnUiThread {
|
||||
MagiskDialog(this).apply {
|
||||
setTitle(R.string.unsupport_nonroot_stub_title)
|
||||
setMessage(R.string.unsupport_nonroot_stub_msg)
|
||||
setButton(MagiskDialog.ButtonType.POSITIVE) {
|
||||
text = R.string.install
|
||||
onClick {
|
||||
withPermission(REQUEST_INSTALL_PACKAGES) {
|
||||
if (!it) {
|
||||
Utils.toast(R.string.install_unknown_denied, Toast.LENGTH_SHORT)
|
||||
showInvalidStateMessage()
|
||||
} else {
|
||||
lifecycleScope.launch {
|
||||
HideAPK.restore(this@BaseMainActivity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setCancelable(false)
|
||||
show()
|
||||
}
|
||||
setCancelable(false)
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,23 +118,10 @@ abstract class BaseMainActivity<Binding : ViewDataBinding> : NavigationActivity<
|
||||
Config.suManager = ""
|
||||
pkg ?: return
|
||||
if (!Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec().isSuccess) {
|
||||
uninstallPkg.launch(pkg)
|
||||
// Wait for the uninstallation to finish
|
||||
latch.await()
|
||||
// Uninstall through Android API
|
||||
uninstallAndWait(pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.topjohnwu.magisk.arch
|
||||
|
||||
import android.Manifest
|
||||
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
|
||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.databinding.Bindable
|
||||
import androidx.databinding.Observable
|
||||
@ -78,7 +80,7 @@ abstract class BaseViewModel(
|
||||
}
|
||||
|
||||
inline fun withExternalRW(crossinline callback: () -> Unit) {
|
||||
withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) {
|
||||
withPermission(WRITE_EXTERNAL_STORAGE) {
|
||||
if (!it) {
|
||||
SnackbarEvent(R.string.external_rw_permission_denied).publish()
|
||||
} else {
|
||||
@ -87,6 +89,17 @@ abstract class BaseViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
inline fun withInstallPermission(crossinline callback: () -> Unit) {
|
||||
withPermission(REQUEST_INSTALL_PACKAGES) {
|
||||
if (!it) {
|
||||
SnackbarEvent(R.string.install_unknown_denied).publish()
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun back() = BackPressEvent().publish()
|
||||
|
||||
fun <Event : ViewEvent> Event.publish() {
|
||||
|
@ -10,6 +10,7 @@ import androidx.core.content.res.use
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.base.BaseActivity
|
||||
@ -74,6 +75,13 @@ abstract class UIActivity<Binding : ViewDataBinding> : BaseActivity(), ViewModel
|
||||
binding.root.rootView.accessibilityDelegate = delegate
|
||||
}
|
||||
|
||||
fun showSnackbar(
|
||||
message: CharSequence,
|
||||
length: Int = Snackbar.LENGTH_SHORT,
|
||||
builder: Snackbar.() -> Unit = {}
|
||||
) = Snackbar.make(snackbarView, message, length)
|
||||
.setAnchorView(snackbarAnchorView).apply(builder).show()
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.requestRefresh()
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.topjohnwu.magisk.core.base
|
||||
|
||||
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
|
||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@ -9,11 +10,16 @@ import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.result.contract.ActivityResultContracts.GetContent
|
||||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||
import com.topjohnwu.magisk.core.utils.RequestInstall
|
||||
import com.topjohnwu.magisk.core.utils.UninstallPackage
|
||||
import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.core.wrap
|
||||
import com.topjohnwu.magisk.ktx.reflectField
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
abstract class BaseActivity : AppCompatActivity() {
|
||||
|
||||
@ -22,6 +28,10 @@ abstract class BaseActivity : AppCompatActivity() {
|
||||
permissionCallback?.invoke(it)
|
||||
permissionCallback = null
|
||||
}
|
||||
private val requestInstall = registerForActivityResult(RequestInstall()) {
|
||||
permissionCallback?.invoke(it)
|
||||
permissionCallback = null
|
||||
}
|
||||
|
||||
private var contentCallback: ((Uri) -> Unit)? = null
|
||||
private val getContent = registerForActivityResult(GetContent()) {
|
||||
@ -29,6 +39,11 @@ abstract class BaseActivity : AppCompatActivity() {
|
||||
contentCallback = null
|
||||
}
|
||||
|
||||
private var uninstallLatch = CountDownLatch(1)
|
||||
private val uninstallPkg = registerForActivityResult(UninstallPackage()) {
|
||||
uninstallLatch.countDown()
|
||||
}
|
||||
|
||||
override fun applyOverrideConfiguration(config: Configuration?) {
|
||||
// Force applying our preferred local
|
||||
config?.setLocale(currentLocale)
|
||||
@ -57,7 +72,11 @@ abstract class BaseActivity : AppCompatActivity() {
|
||||
return
|
||||
}
|
||||
permissionCallback = callback
|
||||
requestPermission.launch(permission)
|
||||
if (permission == REQUEST_INSTALL_PACKAGES) {
|
||||
requestInstall.launch(Unit)
|
||||
} else {
|
||||
requestPermission.launch(permission)
|
||||
}
|
||||
}
|
||||
|
||||
fun getContent(type: String, callback: (Uri) -> Unit) {
|
||||
@ -65,6 +84,13 @@ abstract class BaseActivity : AppCompatActivity() {
|
||||
getContent.launch(type)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun uninstallAndWait(pkg: String) {
|
||||
uninstallLatch = CountDownLatch(1)
|
||||
uninstallPkg.launch(pkg)
|
||||
uninstallLatch.await(3, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
override fun recreate() {
|
||||
startActivity(Intent().setComponent(intent.component))
|
||||
finish()
|
||||
|
@ -21,7 +21,8 @@ import com.topjohnwu.magisk.signing.SignApk
|
||||
import com.topjohnwu.magisk.utils.APKInstall
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
@ -151,9 +152,8 @@ object HideAPK {
|
||||
}
|
||||
}
|
||||
|
||||
@DelicateCoroutinesApi
|
||||
@Suppress("DEPRECATION")
|
||||
fun restore(activity: Activity) {
|
||||
suspend fun restore(activity: Activity) {
|
||||
val dialog = ProgressDialog(activity).apply {
|
||||
setTitle(activity.getString(R.string.restore_img_msg))
|
||||
isIndeterminate = true
|
||||
@ -165,12 +165,12 @@ object HideAPK {
|
||||
launchApp(activity, APPLICATION_ID)
|
||||
dialog.dismiss()
|
||||
}
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
session.install(activity, apk)
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e)
|
||||
return@launch
|
||||
return@withContext
|
||||
}
|
||||
session.waitIntent()?.let { activity.startActivity(it) }
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package com.topjohnwu.magisk.core.utils
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
|
||||
class RequestInstall : ActivityResultContract<Unit, Boolean>() {
|
||||
|
||||
@TargetApi(26)
|
||||
override fun createIntent(context: Context, input: Unit): Intent {
|
||||
// This will only be called on API 26+
|
||||
return Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
|
||||
.setData(Uri.parse("package:${context.packageName}"))
|
||||
}
|
||||
|
||||
override fun parseResult(resultCode: Int, intent: Intent?) =
|
||||
resultCode == Activity.RESULT_OK
|
||||
|
||||
override fun getSynchronousResult(
|
||||
context: Context,
|
||||
input: Unit
|
||||
): SynchronousResult<Boolean>? {
|
||||
if (Build.VERSION.SDK_INT < 26)
|
||||
return SynchronousResult(true)
|
||||
if (context.packageManager.canRequestPackageInstalls())
|
||||
return SynchronousResult(true)
|
||||
return null
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.topjohnwu.magisk.core.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
|
||||
class 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 == Activity.RESULT_OK
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package com.topjohnwu.magisk.events
|
||||
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.topjohnwu.magisk.arch.ActivityExecutor
|
||||
import com.topjohnwu.magisk.arch.UIActivity
|
||||
import com.topjohnwu.magisk.arch.ViewEvent
|
||||
import com.topjohnwu.magisk.utils.TextHolder
|
||||
import com.topjohnwu.magisk.utils.asText
|
||||
|
||||
class SnackbarEvent constructor(
|
||||
private val msg: TextHolder,
|
||||
private val length: Int = Snackbar.LENGTH_SHORT,
|
||||
private val builder: Snackbar.() -> Unit = {}
|
||||
) : ViewEvent(), ActivityExecutor {
|
||||
|
||||
constructor(
|
||||
@StringRes res: Int,
|
||||
length: Int = Snackbar.LENGTH_SHORT,
|
||||
builder: Snackbar.() -> Unit = {}
|
||||
) : this(res.asText(), length, builder)
|
||||
|
||||
constructor(
|
||||
msg: String,
|
||||
length: Int = Snackbar.LENGTH_SHORT,
|
||||
builder: Snackbar.() -> Unit = {}
|
||||
) : this(msg.asText(), length, builder)
|
||||
|
||||
|
||||
private fun snackbar(
|
||||
view: View,
|
||||
anchor: View?,
|
||||
message: String,
|
||||
length: Int,
|
||||
builder: Snackbar.() -> Unit
|
||||
) = Snackbar.make(view, message, length).setAnchorView(anchor).apply(builder).show()
|
||||
|
||||
override fun invoke(activity: UIActivity<*>) {
|
||||
snackbar(activity.snackbarView, activity.snackbarAnchorView,
|
||||
msg.getText(activity.resources).toString(),
|
||||
length, builder)
|
||||
}
|
||||
}
|
@ -5,12 +5,16 @@ import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.navigation.NavDirections
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.topjohnwu.magisk.MainDirections
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.*
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.utils.TextHolder
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.utils.asText
|
||||
import com.topjohnwu.magisk.view.Shortcuts
|
||||
|
||||
class PermissionEvent(
|
||||
@ -67,7 +71,7 @@ class NavigationEvent(
|
||||
) : ViewEvent(), ActivityExecutor {
|
||||
override fun invoke(activity: UIActivity<*>) {
|
||||
(activity as? NavigationActivity<*>)?.apply {
|
||||
if (pop) navigation?.popBackStack()
|
||||
if (pop) navigation.popBackStack()
|
||||
directions.navigate()
|
||||
}
|
||||
}
|
||||
@ -92,3 +96,26 @@ class SelectModuleEvent : ViewEvent(), FragmentExecutor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SnackbarEvent(
|
||||
private val msg: TextHolder,
|
||||
private val length: Int = Snackbar.LENGTH_SHORT,
|
||||
private val builder: Snackbar.() -> Unit = {}
|
||||
) : ViewEvent(), ActivityExecutor {
|
||||
|
||||
constructor(
|
||||
@StringRes res: Int,
|
||||
length: Int = Snackbar.LENGTH_SHORT,
|
||||
builder: Snackbar.() -> Unit = {}
|
||||
) : this(res.asText(), length, builder)
|
||||
|
||||
constructor(
|
||||
msg: String,
|
||||
length: Int = Snackbar.LENGTH_SHORT,
|
||||
builder: Snackbar.() -> Unit = {}
|
||||
) : this(msg.asText(), length, builder)
|
||||
|
||||
override fun invoke(activity: UIActivity<*>) {
|
||||
activity.showSnackbar(msg.getText(activity.resources), length, builder)
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
package com.topjohnwu.magisk.events.dialog
|
||||
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.tasks.HideAPK
|
||||
import com.topjohnwu.magisk.view.MagiskDialog
|
||||
|
||||
class RestoreAppDialog : DialogEvent() {
|
||||
override fun build(dialog: MagiskDialog) {
|
||||
dialog.apply {
|
||||
setTitle(R.string.settings_restore_app_title)
|
||||
setMessage(R.string.restore_app_confirmation)
|
||||
setButton(MagiskDialog.ButtonType.POSITIVE) {
|
||||
text = android.R.string.ok
|
||||
onClick { HideAPK.restore(dialog.ownerActivity!!) }
|
||||
}
|
||||
setButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||
text = android.R.string.cancel
|
||||
}
|
||||
setCancelable(true)
|
||||
}
|
||||
}
|
||||
}
|
@ -123,7 +123,11 @@ class HomeViewModel(
|
||||
fun onDeletePressed() = UninstallDialog().publish()
|
||||
|
||||
fun onManagerPressed() = when (state) {
|
||||
State.LOADED -> withExternalRW { ManagerInstallDialog().publish() }
|
||||
State.LOADED -> withExternalRW {
|
||||
withInstallPermission {
|
||||
ManagerInstallDialog().publish()
|
||||
}
|
||||
}
|
||||
State.LOADING -> SnackbarEvent(R.string.loading).publish()
|
||||
else -> SnackbarEvent(R.string.no_connection).publish()
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import com.topjohnwu.magisk.databinding.set
|
||||
import com.topjohnwu.magisk.di.AppContext
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.utils.asText
|
||||
import com.topjohnwu.magisk.view.MagiskDialog
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
@ -105,6 +106,25 @@ object Hide : BaseSettingsItem.Input() {
|
||||
object Restore : BaseSettingsItem.Blank() {
|
||||
override val title = R.string.settings_restore_app_title.asText()
|
||||
override val description = R.string.settings_restore_app_summary.asText()
|
||||
|
||||
override fun onPressed(view: View, handler: Handler) {
|
||||
handler.onItemPressed(view, this) {
|
||||
MagiskDialog(view.context).apply {
|
||||
setTitle(R.string.settings_restore_app_title)
|
||||
setMessage(R.string.restore_app_confirmation)
|
||||
setButton(MagiskDialog.ButtonType.POSITIVE) {
|
||||
text = android.R.string.ok
|
||||
onClick {
|
||||
handler.onItemAction(view, this@Restore)
|
||||
}
|
||||
}
|
||||
setButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||
text = android.R.string.cancel
|
||||
}
|
||||
setCancelable(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object AddShortcut : BaseSettingsItem.Blank() {
|
||||
|
@ -20,7 +20,6 @@ import com.topjohnwu.magisk.events.AddHomeIconEvent
|
||||
import com.topjohnwu.magisk.events.RecreateEvent
|
||||
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||
import com.topjohnwu.magisk.events.dialog.BiometricEvent
|
||||
import com.topjohnwu.magisk.events.dialog.RestoreAppDialog
|
||||
import com.topjohnwu.magisk.ktx.activity
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.superuser.Shell
|
||||
@ -103,7 +102,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
||||
Theme -> SettingsFragmentDirections.actionSettingsFragmentToThemeFragment().navigate()
|
||||
DenyListConfig -> SettingsFragmentDirections.actionSettingsFragmentToDenyFragment().navigate()
|
||||
SystemlessHosts -> createHosts()
|
||||
Restore -> RestoreAppDialog().publish()
|
||||
Hide, Restore -> withInstallPermission(andThen)
|
||||
AddShortcut -> AddHomeIconEvent().publish()
|
||||
else -> andThen()
|
||||
}
|
||||
@ -114,6 +113,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler {
|
||||
Language -> RecreateEvent().publish()
|
||||
UpdateChannel -> openUrlIfNecessary(view)
|
||||
is Hide -> viewModelScope.launch { HideAPK.hide(view.activity, item.value) }
|
||||
Restore -> viewModelScope.launch { HideAPK.restore(view.activity) }
|
||||
Zygisk -> if (Zygisk.mismatch) SnackbarEvent(R.string.reboot_apply_change).publish()
|
||||
else -> Unit
|
||||
}
|
||||
|
@ -228,6 +228,7 @@
|
||||
<string name="unsupport_nonroot_stub_msg">The hidden Magisk app cannot continue to work because root was lost. Please restore the original APK.</string>
|
||||
<string name="unsupport_nonroot_stub_title">@string/settings_restore_app_title</string>
|
||||
<string name="external_rw_permission_denied">Grant storage permission to enable this functionality</string>
|
||||
<string name="install_unknown_denied">Allow "install unknown apps" to enable this functionality</string>
|
||||
<string name="add_shortcut_title">Add shortcut to home screen</string>
|
||||
<string name="add_shortcut_msg">After hiding this app, its name and icon might become difficult to recognize. Do you want to add a pretty shortcut to the home screen?</string>
|
||||
<string name="app_not_found">No app found to handle this action</string>
|
||||
|
Loading…
Reference in New Issue
Block a user