You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Better stub launch flow
This commit is contained in:
@@ -13,7 +13,6 @@ import androidx.core.view.isVisible
|
||||
import androidx.navigation.NavDirections
|
||||
import com.topjohnwu.magisk.MainDirections
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseMainActivity
|
||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||
import com.topjohnwu.magisk.arch.viewModel
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
@@ -31,7 +30,7 @@ import java.io.File
|
||||
|
||||
class MainViewModel : BaseViewModel()
|
||||
|
||||
class MainActivity : BaseMainActivity<ActivityMainMd2Binding>() {
|
||||
class MainActivity : SplashActivity<ActivityMainMd2Binding>() {
|
||||
|
||||
override val layoutRes = R.layout.activity_main_md2
|
||||
override val viewModel by viewModel<MainViewModel>()
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.topjohnwu.magisk.ui
|
||||
|
||||
import android.Manifest.permission.REQUEST_INSTALL_PACKAGES
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
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.arch.NavigationActivity
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.Const
|
||||
import com.topjohnwu.magisk.core.JobService
|
||||
import com.topjohnwu.magisk.core.di.ServiceLocator
|
||||
import com.topjohnwu.magisk.core.isRunningAsStub
|
||||
import com.topjohnwu.magisk.core.tasks.HideAPK
|
||||
import com.topjohnwu.magisk.core.utils.RootUtils
|
||||
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 kotlinx.coroutines.launch
|
||||
|
||||
@SuppressLint("CustomSplashScreen")
|
||||
abstract class SplashActivity<Binding : ViewDataBinding> : NavigationActivity<Binding>() {
|
||||
|
||||
companion object {
|
||||
private var skipSplash = false
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setTheme(Theme.selected.themeRes)
|
||||
|
||||
if (isRunningAsStub && !skipSplash) {
|
||||
// Manually apply splash theme for stub
|
||||
theme.applyStyle(R.style.StubSplashTheme, true)
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (!isRunningAsStub) {
|
||||
val splashScreen = installSplashScreen()
|
||||
splashScreen.setKeepOnScreenCondition { !skipSplash }
|
||||
}
|
||||
|
||||
if (skipSplash) {
|
||||
showMainUI(savedInstanceState)
|
||||
} else {
|
||||
Shell.getShell(Shell.EXECUTOR) {
|
||||
if (isRunningAsStub && !it.isRoot) {
|
||||
showInvalidStateMessage()
|
||||
return@getShell
|
||||
}
|
||||
preLoad(savedInstanceState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun showMainUI(savedInstanceState: Bundle?)
|
||||
|
||||
@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@SplashActivity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setCancelable(false)
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun preLoad(savedState: Bundle?) {
|
||||
val prevPkg = intent.getStringExtra(Const.Key.PREV_PKG)?.let {
|
||||
// Make sure the calling package matches (prevent DoS)
|
||||
if (it == realCallingPackage)
|
||||
it
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
Config.load(prevPkg)
|
||||
handleRepackage(prevPkg)
|
||||
if (prevPkg != null && !isRunningAsStub) {
|
||||
runOnUiThread {
|
||||
// Might have new configuration loaded, relaunch the activity
|
||||
relaunch()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Notifications.setup(this)
|
||||
JobService.schedule(this)
|
||||
Shortcuts.setupDynamic(this)
|
||||
|
||||
// Pre-fetch network services
|
||||
ServiceLocator.networkService
|
||||
|
||||
// Wait for root service
|
||||
RootUtils.Connection.await()
|
||||
|
||||
runOnUiThread {
|
||||
skipSplash = true
|
||||
if (isRunningAsStub) {
|
||||
// Re-launch main activity without splash theme
|
||||
relaunch()
|
||||
} else {
|
||||
showMainUI(savedState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRepackage(pkg: String?) {
|
||||
if (packageName != APPLICATION_ID) {
|
||||
runCatching {
|
||||
// Hidden, remove com.topjohnwu.magisk if exist as it could be malware
|
||||
packageManager.getApplicationInfo(APPLICATION_ID, 0)
|
||||
Shell.cmd("(pm uninstall $APPLICATION_ID)& >/dev/null 2>&1").exec()
|
||||
}
|
||||
} else {
|
||||
if (Config.suManager.isNotEmpty())
|
||||
Config.suManager = ""
|
||||
pkg ?: return
|
||||
if (!Shell.cmd("(pm uninstall $pkg)& >/dev/null 2>&1").exec().isSuccess) {
|
||||
// Uninstall through Android API
|
||||
uninstallAndWait(pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.topjohnwu.magisk.ui.theme
|
||||
|
||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.events.RecreateEvent
|
||||
import com.topjohnwu.magisk.events.dialog.DarkThemeDialog
|
||||
import com.topjohnwu.magisk.view.TappableHeadlineItem
|
||||
@@ -10,15 +11,13 @@ class ThemeViewModel : BaseViewModel(), TappableHeadlineItem.Listener {
|
||||
val themeHeadline = TappableHeadlineItem.ThemeMode
|
||||
|
||||
override fun onItemPressed(item: TappableHeadlineItem) = when (item) {
|
||||
is TappableHeadlineItem.ThemeMode -> darkModePressed()
|
||||
else -> Unit
|
||||
is TappableHeadlineItem.ThemeMode -> DarkThemeDialog().publish()
|
||||
}
|
||||
|
||||
fun saveTheme(theme: Theme) {
|
||||
theme.select()
|
||||
RecreateEvent().publish()
|
||||
if (!theme.isSelected) {
|
||||
Config.themeOrdinal = theme.ordinal
|
||||
RecreateEvent().publish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun darkModePressed() = DarkThemeDialog().publish()
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user