Migrate to SplashScreen API

This commit is contained in:
topjohnwu 2021-11-05 04:16:58 -07:00
parent 81f57949ed
commit 022c217cfe
14 changed files with 164 additions and 166 deletions

View File

@ -129,5 +129,6 @@ dependencies {
implementation("androidx.fragment:fragment-ktx:1.3.6")
implementation("androidx.transition:transition:1.4.1")
implementation("androidx.core:core-ktx:1.7.0")
implementation("androidx.core:core-splashscreen:1.0.0-alpha02")
implementation("com.google.android.material:material:1.4.0")
}

View File

@ -13,7 +13,7 @@
tools:ignore="UnusedAttribute,GoogleAppIndexingWarning">
<activity
android:name=".core.SplashActivity"
android:name=".ui.MainActivity"
android:exported="true"
android:theme="@style/SplashTheme">
<intent-filter>
@ -26,10 +26,6 @@
</intent-filter>
</activity>
<activity
android:name=".ui.MainActivity"
android:exported="false" />
<activity
android:name=".ui.surequest.SuRequestActivity"
android:directBootAware="true"

View File

@ -0,0 +1,130 @@
package com.topjohnwu.magisk.arch
import android.content.Intent
import android.net.Uri
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.databinding.ViewDataBinding
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
import com.topjohnwu.magisk.R
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.view.MagiskDialog
import com.topjohnwu.magisk.view.Notifications
import com.topjohnwu.magisk.view.Shortcuts
import com.topjohnwu.superuser.Shell
import java.util.concurrent.CountDownLatch
abstract class BaseMainActivity<VM : BaseViewModel, Binding : ViewDataBinding>
: BaseUIActivity<VM, Binding>() {
private val latch = CountDownLatch(1)
companion object {
private var doPreload = true
}
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(Theme.selected.themeRes)
if (isRunningAsStub && doPreload) {
// Manually apply splash theme for stub
if (SDK_INT >= 31) {
theme.applyStyle(R.style.StubSplashTheme, true)
} else {
theme.applyStyle(R.style.SplashTheme, true)
}
}
super.onCreate(savedInstanceState)
if (!isRunningAsStub) {
val splashScreen = installSplashScreen()
splashScreen.setKeepVisibleCondition { doPreload }
}
if (doPreload) {
// Pre-initialize root shell
Shell.getShell(null) {
if (isRunningAsStub && !Shell.rootAccess()) {
showInvalidStateMessage()
return@getShell
}
preLoad()
runOnUiThread {
doPreload = false
if (isRunningAsStub) {
// Re-launch main activity without splash theme
recreate()
} else {
showMainUI(savedInstanceState)
}
}
}
} else {
showMainUI(savedInstanceState)
}
}
abstract fun showMainUI(savedInstanceState: Bundle?)
private fun showInvalidStateMessage() {
runOnUiThread {
MagiskDialog(this)
.applyTitle(R.string.unsupport_nonroot_stub_title)
.applyMessage(R.string.unsupport_nonroot_stub_msg)
.applyButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = R.string.install
onClick { HideAPK.restore(this@BaseMainActivity) }
}
.cancellable(false)
.reveal()
}
}
private fun preLoad() {
val prevPkg = intent.getStringExtra(Const.Key.PREV_PKG)
Config.load(prevPkg)
handleRepackage(prevPkg)
Notifications.setup(this)
JobService.schedule(this)
Shortcuts.setupDynamic(this)
// Pre-fetch network services
ServiceLocator.networkService
}
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.su("(pm uninstall $APPLICATION_ID)& >/dev/null 2>&1").exec()
}
} else {
if (Config.suManager.isNotEmpty())
Config.suManager = ""
pkg ?: return
if (!Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec().isSuccess)
uninstallApp(pkg)
}
}
@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()
}
}

View File

@ -17,14 +17,12 @@ import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.arch.inflater.LayoutInflaterFactory
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.ui.theme.Theme
abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
BaseActivity(), BaseUIComponent<VM> {
protected lateinit var binding: Binding
protected abstract val layoutRes: Int
protected open val themeRes: Int = Theme.selected.themeRes
private val navHostFragment by lazy {
supportFragmentManager.findFragmentById(navHostId) as? NavHostFragment
@ -46,7 +44,6 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
override fun onCreate(savedInstanceState: Bundle?) {
layoutInflater.factory2 = LayoutInflaterFactory(delegate)
setTheme(themeRes)
super.onCreate(savedInstanceState)
startObserveEvents()

View File

@ -1,91 +0,0 @@
package com.topjohnwu.magisk.core
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.tasks.HideAPK
import com.topjohnwu.magisk.di.ServiceLocator
import com.topjohnwu.magisk.ui.MainActivity
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
class SplashActivity : BaseActivity() {
private val latch = CountDownLatch(1)
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.SplashTheme)
super.onCreate(savedInstanceState)
// Pre-initialize root shell
Shell.getShell(null) { initAndStart() }
}
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.su("(pm uninstall $APPLICATION_ID)& >/dev/null 2>&1").exec()
}
} else {
if (Config.suManager.isNotEmpty())
Config.suManager = ""
pkg ?: return
if (!Shell.su("(pm uninstall $pkg)& >/dev/null 2>&1").exec().isSuccess)
uninstallApp(pkg)
}
}
private fun initAndStart() {
if (isRunningAsStub && !Shell.rootAccess()) {
runOnUiThread {
MagiskDialog(this)
.applyTitle(R.string.unsupport_nonroot_stub_title)
.applyMessage(R.string.unsupport_nonroot_stub_msg)
.applyButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = R.string.install
onClick { HideAPK.restore(this@SplashActivity) }
}
.cancellable(false)
.reveal()
}
return
}
val prevPkg = intent.getStringExtra(Const.Key.PREV_PKG)
Config.load(prevPkg)
handleRepackage(prevPkg)
Notifications.setup(this)
JobService.schedule(this)
Shortcuts.setupDynamic(this)
// Pre-fetch network services
ServiceLocator.networkService
DONE = true
startActivity(redirect<MainActivity>())
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()
}
companion object {
var DONE = false
}
}

View File

@ -16,7 +16,7 @@ import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
import androidx.navigation.NavDirections
import com.topjohnwu.magisk.MainDirections
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseUIActivity
import com.topjohnwu.magisk.arch.BaseMainActivity
import com.topjohnwu.magisk.arch.BaseViewModel
import com.topjohnwu.magisk.arch.ReselectionTarget
import com.topjohnwu.magisk.core.*
@ -31,7 +31,7 @@ import java.io.File
class MainViewModel : BaseViewModel()
open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>() {
class MainActivity : BaseMainActivity<MainViewModel, ActivityMainMd2Binding>() {
override val layoutRes = R.layout.activity_main_md2
override val viewModel by viewModel<MainViewModel>()
@ -39,16 +39,7 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
private var isRootFragment = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Make sure Splash is always ran before us
if (!SplashActivity.DONE) {
redirect<SplashActivity>().also { startActivity(it) }
finish()
return
}
override fun showMainUI(savedInstanceState: Bundle?) {
setContentView()
showUnsupportedMessage()
askForHomeShortcut()
@ -83,9 +74,16 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
binding.mainNavigation.setOnItemReselectedListener {
(currentFragment as? ReselectionTarget)?.onReselected()
}
binding.mainNavigation.menu.apply {
findItem(R.id.superuserFragment)?.isEnabled = Utils.showSuperUser()
}
val section =
if (intent.action == Intent.ACTION_APPLICATION_PREFERENCES)
Const.Nav.SETTINGS
else
intent.getStringExtra(Const.Key.OPEN_SECTION)
val section = if (intent.action == Intent.ACTION_APPLICATION_PREFERENCES) Const.Nav.SETTINGS
else intent.getStringExtra(Const.Key.OPEN_SECTION)
getScreen(section)?.navigate()
if (savedInstanceState != null) {
@ -95,13 +93,6 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
}
}
override fun onResume() {
super.onResume()
binding.mainNavigation.menu.apply {
findItem(R.id.superuserFragment)?.isEnabled = Utils.showSuperUser()
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> onBackPressed()

View File

@ -14,6 +14,7 @@ import com.topjohnwu.magisk.core.su.SuCallbackHandler
import com.topjohnwu.magisk.core.su.SuCallbackHandler.REQUEST
import com.topjohnwu.magisk.databinding.ActivityRequestBinding
import com.topjohnwu.magisk.di.viewModel
import com.topjohnwu.magisk.ui.theme.Theme
open class SuRequestActivity : BaseUIActivity<SuRequestViewModel, ActivityRequestBinding>() {
@ -33,6 +34,7 @@ open class SuRequestActivity : BaseUIActivity<SuRequestViewModel, ActivityReques
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
window.setHideOverlayWindows(true)
}
setTheme(Theme.selected.themeRes)
super.onCreate(savedInstanceState)
fun showRequest() {

View File

@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="SplashTheme" parent="Theme.Splash" />
<style name="Foundation" parent="Theme.Foundation" />
</resources>

View File

@ -8,10 +8,9 @@
<style name="Theme.Foundation.Light" parent="Base.V23.Theme.Foundation.Light" />
<style name="Base.V23.Theme.Splash.Light" parent="Base.V21.Theme.Splash.Light">
<item name="android:windowLightStatusBar">false</item>
<style name="Theme.Splash" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/splash_background</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_magisk_padded</item>
</style>
<style name="Theme.Splash.Light" parent="Base.V23.Theme.Splash.Light" />
</resources>
</resources>

View File

@ -15,10 +15,4 @@
<style name="Theme.Foundation" parent="Base.V27.Theme.Foundation" />
<style name="Base.V27.Theme.Splash.Light" parent="Base.V23.Theme.Splash.Light">
<item name="android:windowLightNavigationBar">false</item>
</style>
<style name="Theme.Splash.Light" parent="Base.V27.Theme.Splash.Light" />
</resources>
</resources>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- For stub APK, we need to manually handle the SplashScreen -->
<style name="StubSplashTheme" parent="Theme.SplashScreenBase">
<item name="android:enforceStatusBarContrast">false</item>
<item name="android:enforceNavigationBarContrast">false</item>
<item name="windowSplashScreenBackground">@color/splash_background</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_magisk_padded</item>
</style>
</resources>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="SplashTheme" parent="Theme.Splash.Light" />
<style name="SplashTheme" parent="Theme.Splash" />
<style name="Foundation" parent="Theme.Foundation.Light" />

View File

@ -21,30 +21,10 @@
<style name="Theme.Foundation" parent="Base.V21.Theme.Foundation" />
<style name="Base.V21.Theme.Splash.Light" parent="Theme.MaterialComponents.Light.NoActionBar">
<style name="Theme.Splash" parent="Theme.SplashScreen">
<item name="android:windowBackground">@drawable/ic_splash_activity</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
</style>
<style name="Base.V21.Theme.Splash" parent="Theme.MaterialComponents.NoActionBar">
<item name="android:windowBackground">@drawable/ic_splash_activity</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
</style>
<style name="Theme.Splash.Light" parent="Base.V21.Theme.Splash.Light" />
<style name="Theme.Splash" parent="Base.V21.Theme.Splash" />
<style name="Base.V21.ThemeOverlay.Foundation.Dialog" parent="ThemeOverlay.MaterialComponents.Dialog">
<item name="android:windowMinWidthMajor">@dimen/abc_dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@dimen/abc_dialog_min_width_minor</item>

View File

@ -137,7 +137,7 @@ fun genStubManifest(srcDir: File, outDir: File): String {
))
cmpList.add(Component(
"com.topjohnwu.magisk.core.SplashActivity",
"com.topjohnwu.magisk.ui.MainActivity",
"DownloadActivity",
"""
|<activity
@ -150,15 +150,6 @@ fun genStubManifest(srcDir: File, outDir: File): String {
|</activity>""".ind(2)
))
cmpList.add(Component(
"com.topjohnwu.magisk.ui.MainActivity",
"",
"""
|<activity
| android:name="%s"
| android:exported="false" />""".ind(2)
))
cmpList.add(Component(
"com.topjohnwu.magisk.ui.surequest.SuRequestActivity",
"",