androidandroid-fragmentskotlinonsaveinstancestate

Activity recreated after asking permissions


This is the flow: Activity -> Fragment1 -> Fragment2 -> Fragment3

I have this Fragment3 with a switch view --> When this switch is clicked I call a viewModel method that ask for the required permission to enable this switch.

The problem is that every time I ask for permission and allow or deny, the activity is recreated and the Fragment3 dies, showing up first fragment again (Fragment1)

I've tried using onSaveInstance state but it wasn't clear to me how to retrieve the fragment instance back after asking the permission instead of recreating everything.

Any idea how can I solve this?

Activity:

class Activity : DaggerActivity<DaggerComponent>() {

private val viewModel: ActivityViewModel by injectionViewModels {
    activityComponent.activityViewModel
}

override fun onSafeCreate(savedInstanceState: Bundle?) {
    super.onSafeCreate(savedInstanceState)
    setContentView(R.layout.single_fragment_activity)

    if (savedInstanceState != null) {
        onRestoreInstanceState(savedInstanceState)
    } else
        observeViewModel()
}

override fun onSafeResume() {
    super.onSafeResume()
    viewModel.onCheckFeatureState()
}

private fun observeViewModel() {
    observe(viewModel.areAllPermissionsGiven) { areGiven ->
        if (areGiven) {
            replaceFragment(SettingsFragment(), R.id.fragment_container, false)
        } else {
            replaceFragment(PermissionsFragment(), R.id.fragment_container, false)
        }
    }
}}

MyFragment:

class MyFragment : DaggerFragment<DaggerComponent>() {

private val viewModel: FragmentViewModel by injectionActivityViewModels {
    activityComponent.fragmentViewModel
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.fragment, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    initialize()
}

private fun initialize() {
    viewModel.onViewCreated()
    toolbar.setNavigationOnClickListener { activity?.onBackPressed() }
    initViews()
    initObservables()
}

private fun initViews() {
    switch.onClick {
        viewModel.onToggleMonitoringSecurityClick()
    }
}

private fun initObservables() {
    observe(viewModel.state) { state ->
        switch.isChecked = state.isEnabled
        switchText.isGone = state.hasPermission
    }
}}

The code I use to ask for permission and enable the feature using shared preferences:

class Option @AssistedInject constructor(
@Assisted private val permission: Permission,
@Assisted private val dataSource: DataSource,
private val permissionStatus: GetPermissionStatusUseCase,
private val permissionRequest: RequestPermissionUseCase) {
fun isEnabled() = dataSource.isEnabled && hasPermission

suspend fun enable() = withContext(Dispatchers.IO) {
    val enable = hasPermission || (permissionRequest(permission) is Response.Success)

    if (enable) {
        dataSource.isEnabled = true
    }

    enable
}

fun disable() {
    dataSource.isEnabled = false
}

suspend fun toggle() {
    if (isEnabled()) {
        disable()
    } else {
        enable()
    }
}

val hasPermission
    get() = permissionStatus.invoke(permission) == PermissionStatus.ENABLED

@AssistedInject.Factory
interface Factory {
    fun create(permission: Permission, dataSource: DataSource): Option
}}

I use the above domain object code on a useCase which provides to viewModel the returning value of having permission or not.


Solution

  • This is how I solved my problem:

    in Manifest add this line for activities

    android:launchMode="singleTop"
    

    for example:

    <activity
        android:name=".Activity"
        android:label="@string/my_activity"
        android:launchMode="singleTop"
    </activity>