Commit 1b881322 authored by Viktor De Pasquale's avatar Viktor De Pasquale Committed by John Wu

Updated the app to use navigation components instead of custom solution

Welcome to mid 2018.
parent 922e36cf
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
plugins {
id("com.android.application")
id("kotlin-android")
id("kotlin-android-extensions")
id("kotlin-kapt")
id("androidx.navigation.safeargs.kotlin")
}
kapt {
correctErrorTypes = true
......@@ -22,7 +25,7 @@ android {
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.incremental":"true"]
arguments = ["room.incremental": "true"]
}
}
}
......@@ -65,7 +68,6 @@ dependencies {
implementation 'com.github.topjohnwu:jtar:1.0.0'
implementation 'com.jakewharton.timber:timber:4.7.1'
implementation 'com.ncapdevi:frag-nav:3.2.0'
implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.6'
implementation 'io.reactivex.rxjava2:rxjava:2.2.18'
......@@ -116,7 +118,6 @@ dependencies {
implementation "androidx.room:room-rxjava2:${vRoom}"
kapt "androidx.room:room-compiler:${vRoom}"
def vNav = '2.2.1'
implementation "androidx.navigation:navigation-fragment-ktx:${vNav}"
implementation "androidx.navigation:navigation-ui-ktx:${vNav}"
......
......@@ -6,6 +6,7 @@ import android.content.pm.ActivityInfo
import android.net.Uri
import android.os.Bundle
import androidx.core.net.toUri
import androidx.navigation.NavController
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.intent
......@@ -16,7 +17,6 @@ import com.topjohnwu.magisk.model.events.PermissionEvent
import com.topjohnwu.magisk.model.events.SnackbarEvent
import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.ui.base.BaseUIActivity
import com.topjohnwu.magisk.ui.base.CompatNavigationDelegate
import org.koin.androidx.viewmodel.ext.android.viewModel
import java.io.File
......@@ -25,8 +25,7 @@ open class FlashActivity : BaseUIActivity<FlashViewModel, ActivityFlashBinding>(
override val layoutRes: Int = R.layout.activity_flash
override val viewModel: FlashViewModel by viewModel()
override val navigation: CompatNavigationDelegate<BaseUIActivity<FlashViewModel, ActivityFlashBinding>>? =
null
override val navigation: NavController? = null
override fun onCreate(savedInstanceState: Bundle?) {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
......
......@@ -7,6 +7,7 @@ import android.os.Build
import android.os.Bundle
import android.view.Window
import android.view.WindowManager
import androidx.navigation.NavController
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.su.SuCallbackHandler
import com.topjohnwu.magisk.core.su.SuCallbackHandler.REQUEST
......@@ -15,7 +16,6 @@ import com.topjohnwu.magisk.model.events.DieEvent
import com.topjohnwu.magisk.model.events.ViewActionEvent
import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.ui.base.BaseUIActivity
import com.topjohnwu.magisk.ui.base.CompatNavigationDelegate
import org.koin.androidx.viewmodel.ext.android.viewModel
open class SuRequestActivity : BaseUIActivity<SuRequestViewModel, ActivityRequestBinding>() {
......@@ -23,8 +23,7 @@ open class SuRequestActivity : BaseUIActivity<SuRequestViewModel, ActivityReques
override val layoutRes: Int = R.layout.activity_request
override val viewModel: SuRequestViewModel by viewModel()
override val navigation: CompatNavigationDelegate<BaseUIActivity<SuRequestViewModel, ActivityRequestBinding>>? =
null
override val navigation: NavController? = null
override fun onBackPressed() {
viewModel.denyPressed()
......
package com.topjohnwu.magisk.model.navigation
import android.os.Bundle
import androidx.annotation.AnimRes
import androidx.annotation.AnimatorRes
import androidx.fragment.app.Fragment
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.model.events.ActivityExecutor
import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.ui.base.BaseUIActivity
import kotlin.reflect.KClass
@DslMarker
annotation class NavigationDslMarker
class MagiskNavigationEvent(
val navDirections: MagiskNavDirectionsBuilder,
val navOptions: MagiskNavOptions,
val animOptions: MagiskAnimBuilder
) : ViewEvent(), ActivityExecutor {
companion object {
operator fun invoke(builder: Builder.() -> Unit) = Builder().apply(builder).build()
}
override fun invoke(activity: BaseActivity) {
if (activity !is BaseUIActivity<*, *>) return
activity.navigation?.navigateTo(this)
}
@NavigationDslMarker
class Builder {
private var animOptions: MagiskAnimBuilder = MagiskAnimBuilder()
private var navOptions: MagiskNavOptions = MagiskNavOptions()
private val directionsBuilder = MagiskNavDirectionsBuilder()
fun args(builder: Bundle.() -> Unit) = directionsBuilder.args(builder)
fun navAnim(builder: MagiskAnimBuilder.() -> Unit) {
animOptions = MagiskAnimBuilder().apply(builder)
}
fun navOptions(builder: MagiskNavOptions.() -> Unit) {
navOptions = MagiskNavOptions().apply(builder)
}
fun navDirections(builder: MagiskNavDirectionsBuilder.() -> Unit) {
directionsBuilder.apply(builder)
}
internal fun build() = MagiskNavigationEvent(directionsBuilder, navOptions, animOptions)
}
}
@NavigationDslMarker
class MagiskNavDirectionsBuilder {
var destination: KClass<out Fragment>? = null
var isActivity: Boolean = false
val args: Bundle = Bundle()
fun args(builder: Bundle.() -> Unit) = args.apply(builder)
}
@NavigationDslMarker
class MagiskNavOptions {
var popUpTo: KClass<*>? = null
var inclusive: Boolean = false
var clearTask: Boolean = false
var singleTop: Boolean = false
}
@NavigationDslMarker
class MagiskAnimBuilder {
@AnimRes
@AnimatorRes
var enter = 0
@AnimRes
@AnimatorRes
var exit = 0
@AnimRes
@AnimatorRes
var popEnter = 0
@AnimRes
@AnimatorRes
var popExit = 0
val anySet: Boolean get() = enter != 0 || exit != 0 || popEnter != 0 || popExit != 0
}
......@@ -6,80 +6,9 @@ import android.os.Build
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.intent
import com.topjohnwu.magisk.ui.MainActivity
import com.topjohnwu.magisk.ui.hide.HideFragment
import com.topjohnwu.magisk.ui.home.HomeFragment
import com.topjohnwu.magisk.ui.install.InstallFragment
import com.topjohnwu.magisk.ui.log.LogFragment
import com.topjohnwu.magisk.ui.module.ModuleFragment
import com.topjohnwu.magisk.ui.safetynet.SafetynetFragment
import com.topjohnwu.magisk.ui.settings.SettingsFragment
import com.topjohnwu.magisk.ui.superuser.SuperuserFragment
import com.topjohnwu.magisk.ui.theme.ThemeFragment
object Navigation {
fun home() = MagiskNavigationEvent {
navDirections {
destination = HomeFragment::class
}
navOptions {
popUpTo = HomeFragment::class
}
}
fun superuser() = MagiskNavigationEvent {
navDirections {
destination = SuperuserFragment::class
}
}
fun modules() = MagiskNavigationEvent {
navDirections {
destination = ModuleFragment::class
}
}
fun hide() = MagiskNavigationEvent {
navDirections {
destination = HideFragment::class
}
}
fun safetynet() = MagiskNavigationEvent {
navDirections { destination = SafetynetFragment::class }
}
fun log() = MagiskNavigationEvent {
navDirections {
destination = LogFragment::class
}
}
fun settings() = MagiskNavigationEvent {
navDirections {
destination = SettingsFragment::class
}
}
fun install() = MagiskNavigationEvent {
navDirections { destination = InstallFragment::class }
}
fun theme() = MagiskNavigationEvent {
navDirections { destination = ThemeFragment::class }
}
fun fromSection(section: String) = when (section) {
"superuser" -> superuser()
"modules" -> modules()
"magiskhide" -> hide()
"log" -> log()
"settings" -> settings()
else -> home()
}
// redesign starts here
fun start(launchIntent: Intent, context: Context) {
context.intent<MainActivity>()
.putExtra(
......@@ -92,10 +21,6 @@ object Navigation {
.also { context.startActivity(it) }
}
object Main {
const val OPEN_NAV = 1
}
private val ACTION_APPLICATION_PREFERENCES
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Intent.ACTION_APPLICATION_PREFERENCES
......
package com.topjohnwu.magisk.model.navigation
import androidx.navigation.NavDirections
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.model.events.ActivityExecutor
import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.ui.base.BaseUIActivity
class NavigationWrapper(
private val directions: NavDirections
) : ViewEvent(), ActivityExecutor {
override fun invoke(activity: BaseActivity) {
if (activity !is BaseUIActivity<*, *>) return
activity.apply {
directions.navigate()
}
}
}
\ No newline at end of file
......@@ -7,47 +7,31 @@ import android.view.ViewTreeObserver
import android.view.WindowManager
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.graphics.Insets
import androidx.core.view.forEach
import androidx.core.view.setPadding
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment
import com.google.android.material.card.MaterialCardView
import com.ncapdevi.fragnav.FragNavController
import com.topjohnwu.magisk.MainDirections
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.databinding.ActivityMainMd2Binding
import com.topjohnwu.magisk.extensions.startAnimations
import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.ui.base.BaseUIActivity
import com.topjohnwu.magisk.ui.base.CompatNavigationDelegate
import com.topjohnwu.magisk.ui.home.HomeFragment
import com.topjohnwu.magisk.ui.log.LogFragment
import com.topjohnwu.magisk.ui.module.ModuleFragment
import com.topjohnwu.magisk.ui.superuser.SuperuserFragment
import com.topjohnwu.magisk.ui.home.HomeFragmentDirections
import com.topjohnwu.magisk.utils.HideBottomViewOnScrollBehavior
import com.topjohnwu.magisk.utils.HideTopViewOnScrollBehavior
import com.topjohnwu.magisk.utils.HideableBehavior
import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.superuser.Shell
import org.koin.androidx.viewmodel.ext.android.viewModel
import kotlin.reflect.KClass
open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(),
FragNavController.TransactionListener {
open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>() {
override val layoutRes = R.layout.activity_main_md2
override val viewModel by viewModel<MainViewModel>()
override val navHost: Int = R.id.main_nav_host
override val navigation by lazy { CompatNavigationDelegate(this, this) }
override val baseFragments: List<KClass<out Fragment>> = listOf(
HomeFragment::class,
ModuleFragment::class,
SuperuserFragment::class,
LogFragment::class
)
//This temporarily fixes unwanted feature of BottomNavigationView - where the view applies
//padding on itself given insets are not consumed beforehand. Unfortunately the listener
//implementation doesn't favor us against the design library, so on re-create it's often given
......@@ -56,6 +40,9 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
binding.mainNavigation.setPadding(0)
}
protected var isRoot = true
private set
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -70,6 +57,25 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
navigation?.addOnDestinationChangedListener { controller, destination, arguments ->
isRoot = when (destination.id) {
R.id.homeFragment,
R.id.modulesFragment,
R.id.superuserFragment,
R.id.logFragment -> true
else -> false
}
setDisplayHomeAsUpEnabled(!isRoot)
requestNavigationHidden(!isRoot)
binding.mainNavigation.menu.forEach {
if (it.itemId == destination.id) {
it.isChecked = true
}
}
}
setSupportActionBar(binding.mainToolbar)
binding.mainToolbarWrapper.updateLayoutParams<CoordinatorLayout.LayoutParams> {
......@@ -80,28 +86,26 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
}
binding.mainNavigation.setOnNavigationItemSelectedListener {
when (it.itemId) {
R.id.homeFragment -> Navigation.home()
R.id.modulesFragment -> Navigation.modules()
R.id.superuserFragment -> Navigation.superuser()
R.id.logFragment -> Navigation.log()
R.id.homeFragment -> MainDirections.actionHomeFragment()
R.id.modulesFragment -> MainDirections.actionModuleFragment()
R.id.superuserFragment -> MainDirections.actionSuperuserFragment()
R.id.logFragment -> MainDirections.actionLogFragment()
else -> throw NotImplementedError("Id ${it.itemId} is not defined as selectable")
}.dispatchOnSelf()
}.navigate()
true
}
binding.mainNavigation.setOnNavigationItemReselectedListener {
navigation.onReselected()
(currentFragment as? ReselectionTarget)?.onReselected()
}
binding.mainNavigation.viewTreeObserver.addOnGlobalLayoutListener(navObserver)
if (intent.getBooleanExtra(Const.Key.OPEN_SETTINGS, false)) {
Navigation.settings().dispatchOnSelf()
HomeFragmentDirections.actionHomeFragmentToSettingsFragment().navigate()
}
if (savedInstanceState != null) {
onTabTransaction(null, -1)
if (!navigation.isRoot) {
if (!isRoot) {
requestNavigationHidden()
}
}
......@@ -129,17 +133,6 @@ open class MainActivity : BaseUIActivity<MainViewModel, ActivityMainMd2Binding>(
return true
}
override fun onTabTransaction(fragment: Fragment?, index: Int) =
onFragmentTransaction(fragment, FragNavController.TransactionType.PUSH)
override fun onFragmentTransaction(
fragment: Fragment?,
transactionType: FragNavController.TransactionType
) {
setDisplayHomeAsUpEnabled(!navigation.isRoot)
requestNavigationHidden(!navigation.isRoot)
}
override fun peekSystemWindowInsets(insets: Insets) {
viewModel.insets.value = insets
}
......
......@@ -9,7 +9,8 @@ import androidx.core.graphics.Insets
import androidx.databinding.DataBindingUtil
import androidx.databinding.OnRebindCallback
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import androidx.navigation.NavDirections
import androidx.navigation.findNavController
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.base.BaseActivity
......@@ -19,7 +20,6 @@ import com.topjohnwu.magisk.model.events.EventHandler
import com.topjohnwu.magisk.model.events.SnackbarEvent
import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.ui.theme.Theme
import kotlin.reflect.KClass
abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBinding> :
BaseActivity(), CompatView<ViewModel>, EventHandler {
......@@ -28,20 +28,30 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi
protected abstract val layoutRes: Int
protected open val themeRes: Int = Theme.selected.themeRes
private val navHostFragment get() = supportFragmentManager.findFragmentById(navHost)
private val topFragment get() = navHostFragment?.childFragmentManager?.fragments?.getOrNull(0)
protected val currentFragment get() = topFragment as? BaseUIFragment<*, *>
override val viewRoot: View get() = binding.root
override val navigation by lazy { CompatNavigationDelegate(this) as CompatNavigationDelegate? }
override val navigation by lazy {
kotlin.runCatching { findNavController(navHost) }.getOrNull()
}
private val delegate by lazy { CompatDelegate(this) }
open val navHost: Int = 0
open val snackbarView get() = binding.root
open val baseFragments = listOf<KClass<out Fragment>>()
init {
val theme = Config.darkThemeExtended
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?) {
setTheme(themeRes)
super.onCreate(savedInstanceState)
......@@ -61,12 +71,6 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi
})
delegate.onCreate()
navigation?.onCreate(savedInstanceState)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
navigation?.onActivityResult(requestCode, resultCode, data)
}
override fun onResume() {
......@@ -74,11 +78,6 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi
delegate.onResume()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
navigation?.onSaveInstanceState(outState)
}
override fun onEventDispatched(event: ViewEvent) {
delegate.onEventExecute(event, this)
when (event) {
......@@ -87,7 +86,7 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi
}
override fun onBackPressed() {
if (navigation == null || navigation?.onBackPressed()?.not() == true) {
if (navigation == null || currentFragment?.onBackPressed()?.not() == true) {
super.onBackPressed()
}
}
......@@ -97,4 +96,9 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi
}
protected fun ViewEvent.dispatchOnSelf() = onEventDispatched(this)
fun NavDirections.navigate() {
navigation?.navigate(this)
}
}
......@@ -9,6 +9,7 @@ import androidx.databinding.DataBindingUtil
import androidx.databinding.OnRebindCallback
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import androidx.navigation.NavDirections
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.extensions.startAnimations
import com.topjohnwu.magisk.model.events.EventHandler
......@@ -22,7 +23,7 @@ abstract class BaseUIFragment<ViewModel : BaseViewModel, Binding : ViewDataBindi
protected abstract val layoutRes: Int
override val viewRoot: View get() = binding.root
override val navigation by lazy { activity.navigation }
override val navigation get() = activity.navigation
private val delegate by lazy { CompatDelegate(this) }
override fun consumeSystemWindowInsets(insets: Insets) = insets
......@@ -78,4 +79,8 @@ abstract class BaseUIFragment<ViewModel : BaseViewModel, Binding : ViewDataBindi
protected fun ViewEvent.dispatchOnSelf() = delegate.onEventExecute(this, this@BaseUIFragment)
fun NavDirections.navigate() {
navigation?.navigate(this)
}
}
......@@ -7,11 +7,13 @@ import androidx.databinding.PropertyChangeRegistry
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.navigation.NavDirections
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.extensions.doOnSubscribeUi
import com.topjohnwu.magisk.model.events.*
import com.topjohnwu.magisk.model.navigation.NavigationWrapper
import com.topjohnwu.magisk.model.observer.Observer
import com.topjohnwu.magisk.utils.KObservableField
import io.reactivex.*
......@@ -99,6 +101,10 @@ abstract class BaseViewModel(
_viewEvents.postValue(SimpleViewEvent(this))
}
fun NavDirections.publish() {
_viewEvents.postValue(NavigationWrapper(this))
}
fun Disposable.add() {
disposables.add(this)
}
......
package com.topjohnwu.magisk.ui.base
import android.content.Intent
import android.os.Bundle
import com.ncapdevi.fragnav.FragNavController
import com.ncapdevi.fragnav.FragNavTransactionOptions
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.model.navigation.MagiskAnimBuilder
import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent
import com.topjohnwu.magisk.ui.ReselectionTarget
import timber.log.Timber
class CompatNavigationDelegate<out Source>(
private val source: Source,
private val listener: FragNavController.TransactionListener? = null
) : FragNavController.RootFragmentListener where Source : BaseUIActivity<*, *> {
private val controller by lazy {
check(source.navHost != 0) { "Did you forget to override \"navHostId\"?" }
FragNavController(source.supportFragmentManager, source.navHost)
}
val isRoot get() = controller.isRootFragment
//region Listener
override val numberOfRootFragments: Int
get() = source.baseFragments.size
override fun getRootFragment(index: Int) =
source.baseFragments[index].java.newInstance()
//endregion
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
controller.currentFrag?.onActivityResult(requestCode, resultCode, data)
}
fun onCreate(savedInstanceState: Bundle?) = controller.run {
rootFragmentListener = this@CompatNavigationDelegate
transactionListener = listener
initialize(0, savedInstanceState)
}
fun onSaveInstanceState(outState: Bundle) =
controller.onSaveInstanceState(outState)
fun onReselected() {
(controller.currentFrag as? ReselectionTarget)?.onReselected()
}
fun onBackPressed(): Boolean {
val fragment = controller.currentFrag as? BaseUIFragment<*, *>
if (fragment?.onBackPressed() == true) {
return true
}
return runCatching { controller.popFragment() }.fold({ true }, { false })
}
// ---
fun navigateTo(event: MagiskNavigationEvent) {
val directions = event.navDirections
controller.defaultTransactionOptions = FragNavTransactionOptions.newBuilder()
.customAnimations(event.animOptions)
.build()
controller.currentStack
?.indexOfFirst { it.javaClass == event.navOptions.popUpTo }
?.takeIf { it != -1 } // invalidate if class is not found
?.let { if (event.navOptions.inclusive) it + 1 else it }
?.let { controller.popFragments(it) }
when (directions.isActivity) {
true -> navigateToActivity(event)
else -> navigateToFragment(event)
}
}
private fun navigateToActivity(event: MagiskNavigationEvent) {
val destination = event.navDirections.destination?.java ?: let {
Timber.e("Cannot navigate to null destination")
return
}
val options = event.navOptions
Intent(source, destination)
.putExtras(event.navDirections.args)
.apply {
if (options.singleTop) addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
if (options.clearTask) addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
}
.let { source.startActivity(it) }
}
private fun navigateToFragment(event: MagiskNavigationEvent) {
val destination = event.navDirections.destination ?: let {
Timber.e("Cannot navigate to null destination")
return
}
source.baseFragments
.indexOfFirst { it == destination }
.takeIf { it >= 0 }
?.let { controller.switchTab(it) } ?: destination.java.newInstance()
.also { it.arguments = event.navDirections.args }
.let { controller.pushFragment(it) }
}
private fun FragNavTransactionOptions.Builder.customAnimations(options: MagiskAnimBuilder) =
apply {
if (!options.anySet) customAnimations(
R.anim.fragment_enter,
R.anim.fragment_exit,
R.anim.fragment_enter_pop,
R.anim.fragment_exit_pop
) else customAnimations(
options.enter,
options.exit,
options.popEnter,
options.popExit
)
}
}
......@@ -2,12 +2,13 @@ package com.topjohnwu.magisk.ui.base
import android.view.View
import androidx.core.graphics.Insets
import androidx.navigation.NavController
internal interface CompatView<ViewModel : BaseViewModel> {
val viewRoot: View
val viewModel: ViewModel
val navigation: CompatNavigationDelegate<*>?
val navigation: NavController?
fun peekSystemWindowInsets(insets: Insets) = Unit
fun consumeSystemWindowInsets(insets: Insets): Insets? = null
......
......@@ -5,7 +5,6 @@ import android.view.*
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.FragmentHomeMd2Binding
import com.topjohnwu.magisk.model.events.RebootEvent
import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.ui.base.BaseUIFragment
import com.topjohnwu.superuser.Shell
import org.koin.androidx.viewmodel.ext.android.viewModel
......@@ -46,7 +45,8 @@ class HomeFragment : BaseUIFragment<HomeViewModel, FragmentHomeMd2Binding>() {
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_settings -> Navigation.settings().dispatchOnSelf()
R.id.action_settings -> HomeFragmentDirections.actionHomeFragmentToSettingsFragment()
.navigate()
R.id.action_reboot -> RebootEvent.inflateMenu(activity).show()
else -> null
}?.let { true } ?: super.onOptionsItemSelected(item)
......
......@@ -22,7 +22,6 @@ import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.model.events.dialog.EnvFixDialog
import com.topjohnwu.magisk.model.events.dialog.ManagerInstallDialog
import com.topjohnwu.magisk.model.events.dialog.UninstallDialog
import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.ui.base.BaseViewModel
import com.topjohnwu.magisk.ui.base.itemBindingOf
import com.topjohnwu.magisk.utils.KObservableField
......@@ -114,7 +113,7 @@ class HomeViewModel(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
).map { check(it);it }
.subscribeK { Navigation.install().publish() }
.subscribeK { HomeFragmentDirections.actionHomeFragmentToInstallFragment().publish() }
.add()
fun toggle(kof: KObservableField<Boolean>) = kof.toggle()
......
......@@ -19,7 +19,6 @@ import com.topjohnwu.magisk.model.entity.recycler.SettingsItem
import com.topjohnwu.magisk.model.events.PermissionEvent
import com.topjohnwu.magisk.model.events.RecreateEvent
import com.topjohnwu.magisk.model.events.dialog.BiometricDialog
import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.ui.base.BaseViewModel
import com.topjohnwu.magisk.ui.base.adapterOf
import com.topjohnwu.magisk.ui.base.diffListOf
......@@ -95,7 +94,7 @@ class SettingsViewModel(
override fun onItemChanged(view: View, item: SettingsItem) = when (item) {
// use only instances you want, don't declare everything
is Theme -> Navigation.theme().publish()
is Theme -> SettingsFragmentDirections.actionSettingsFragmentToThemeFragment().publish()
is Language -> RecreateEvent().publish()
is UpdateChannel -> openUrlIfNecessary(view)
......
......@@ -20,7 +20,6 @@ import com.topjohnwu.magisk.model.events.PolicyUpdateEvent
import com.topjohnwu.magisk.model.events.SnackbarEvent
import com.topjohnwu.magisk.model.events.dialog.BiometricDialog
import com.topjohnwu.magisk.model.events.dialog.SuperuserRevokeDialog
import com.topjohnwu.magisk.model.navigation.Navigation
import com.topjohnwu.magisk.ui.base.BaseViewModel
import com.topjohnwu.magisk.ui.base.adapterOf
import com.topjohnwu.magisk.ui.base.diffListOf
......@@ -85,8 +84,11 @@ class SuperuserViewModel(
else -> Unit
}
private fun safetynetPressed() = Navigation.safetynet().publish()
private fun hidePressed() = Navigation.hide().publish()
private fun safetynetPressed() =
SuperuserFragmentDirections.actionSuperuserFragmentToSafetynetFragment().publish()
private fun hidePressed() =
SuperuserFragmentDirections.actionSuperuserFragmentToHideFragment().publish()
fun deletePressed(item: PolicyItem) {
fun updateState() = deletePolicy(item.item)
......
......@@ -19,10 +19,13 @@
android:paddingRight="@{viewModel.insets.right}"
tools:ignore="RtlHardcoded">
<androidx.fragment.app.FragmentContainerView
<fragment
android:id="@+id/main_nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/main" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/main_toolbar_wrapper"
......
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
app:startDestination="@id/homeFragment">
<fragment
android:id="@+id/hideFragment"
android:name="com.topjohnwu.magisk.ui.hide.HideFragment"
android:label="HideFragment"
tools:layout="@layout/fragment_hide_md2" />
<fragment
android:id="@+id/homeFragment"
android:name="com.topjohnwu.magisk.ui.home.HomeFragment"
android:label="HomeFragment"
tools:layout="@layout/fragment_home_md2">
<action
android:id="@+id/action_homeFragment_to_settingsFragment"
app:destination="@id/settingsFragment"
app:enterAnim="@anim/fragment_enter"
app:exitAnim="@anim/fragment_exit"
app:popEnterAnim="@anim/fragment_enter_pop"
app:popExitAnim="@anim/fragment_exit_pop" />
<action
android:id="@+id/action_homeFragment_to_installFragment"
app:destination="@id/installFragment"
app:enterAnim="@anim/fragment_enter"
app:exitAnim="@anim/fragment_exit"
app:popEnterAnim="@anim/fragment_enter_pop"
app:popExitAnim="@anim/fragment_exit_pop" />
</fragment>
<fragment
android:id="@+id/installFragment"
android:name="com.topjohnwu.magisk.ui.install.InstallFragment"
android:label="InstallFragment"
tools:layout="@layout/fragment_install_md2" />
<fragment
android:id="@+id/logFragment"
android:name="com.topjohnwu.magisk.ui.log.LogFragment"
android:label="LogFragment"
tools:layout="@layout/fragment_log_md2" />
<fragment
android:id="@+id/modulesFragment"
android:name="com.topjohnwu.magisk.ui.module.ModuleFragment"
android:label="ModuleFragment"
tools:layout="@layout/fragment_module_md2" />
<fragment
android:id="@+id/safetynetFragment"
android:name="com.topjohnwu.magisk.ui.safetynet.SafetynetFragment"
android:label="SafetynetFragment"
tools:layout="@layout/fragment_safetynet_md2" />
<fragment
android:id="@+id/settingsFragment"
android:name="com.topjohnwu.magisk.ui.settings.SettingsFragment"
android:label="SettingsFragment"
tools:layout="@layout/fragment_settings_md2">
<action
android:id="@+id/action_settingsFragment_to_themeFragment"
app:destination="@id/themeFragment"
app:enterAnim="@anim/fragment_enter"
app:exitAnim="@anim/fragment_exit"
app:popEnterAnim="@anim/fragment_enter_pop"
app:popExitAnim="@anim/fragment_exit_pop" />
</fragment>
<fragment
android:id="@+id/superuserFragment"
android:name="com.topjohnwu.magisk.ui.superuser.SuperuserFragment"
android:label="SuperuserFragment"
tools:layout="@layout/fragment_superuser_md2">
<action
android:id="@+id/action_superuserFragment_to_safetynetFragment"
app:destination="@id/safetynetFragment"
app:enterAnim="@anim/fragment_enter"
app:exitAnim="@anim/fragment_exit"
app:popEnterAnim="@anim/fragment_enter_pop"
app:popExitAnim="@anim/fragment_exit_pop" />
<action
android:id="@+id/action_superuserFragment_to_hideFragment"
app:destination="@id/hideFragment"
app:enterAnim="@anim/fragment_enter"
app:exitAnim="@anim/fragment_exit"
app:popEnterAnim="@anim/fragment_enter_pop"
app:popExitAnim="@anim/fragment_exit_pop" />
</fragment>
<fragment
android:id="@+id/themeFragment"
android:name="com.topjohnwu.magisk.ui.theme.ThemeFragment"
android:label="ThemeFragment"
tools:layout="@layout/fragment_theme_md2" />
<action
android:id="@+id/action_homeFragment"
app:destination="@id/homeFragment"
app:enterAnim="@anim/fragment_enter"
app:exitAnim="@anim/fragment_exit"
app:popEnterAnim="@anim/fragment_enter_pop"
app:popExitAnim="@anim/fragment_exit_pop"
app:popUpTo="@id/homeFragment"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_superuserFragment"
app:destination="@id/superuserFragment"
app:enterAnim="@anim/fragment_enter"
app:exitAnim="@anim/fragment_exit"
app:popEnterAnim="@anim/fragment_enter_pop"
app:popExitAnim="@anim/fragment_exit_pop"
app:popUpTo="@id/superuserFragment"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_logFragment"
app:destination="@id/logFragment"
app:enterAnim="@anim/fragment_enter"
app:exitAnim="@anim/fragment_exit"
app:popEnterAnim="@anim/fragment_enter_pop"
app:popExitAnim="@anim/fragment_exit_pop"
app:popUpTo="@id/logFragment"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_moduleFragment"
app:destination="@id/modulesFragment"
app:enterAnim="@anim/fragment_enter"
app:exitAnim="@anim/fragment_exit"
app:popEnterAnim="@anim/fragment_enter_pop"
app:popExitAnim="@anim/fragment_exit_pop"
app:popUpTo="@id/modulesFragment"
app:popUpToInclusive="true" />
</navigation>
\ No newline at end of file
......@@ -8,6 +8,7 @@ if (configPath.exists())
buildscript {
ext.vKotlin = '1.3.70'
ext.vNav = '2.3.0-alpha03'
repositories {
google()
......@@ -18,6 +19,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.6.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${vKotlin}"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$vNav"
// NOTE: Do not place your application dependencies here; they belong
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment