Better stub launch flow

This commit is contained in:
topjohnwu
2022-06-05 19:15:43 -07:00
parent fa60daf9b5
commit 317153c53a
7 changed files with 55 additions and 38 deletions
@@ -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()
}