Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#310] Ensure NavTarget and State are parcelable #311

Open
wants to merge 1 commit into
base: 1.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Pending changes

- [#311](https://github.com/bumble-tech/appyx/pull/311) – **Breaking change**: `NavTarget` and `State` are now explicitly `Parcelable`. Please see this PR to see approaches to resolve any compilation issues you may notice.
- [#287](https://github.com/bumble-tech/appyx/pull/287) – **Added**: Introduced a new `rememberCombinedHandler` implementation that takes an immutable list to avoid non-skippable compositions. The previous implementation is now deprecated.
- [#287](https://github.com/bumble-tech/appyx/pull/287) – **Added**: `ImmutableList` has been added to avoid non-skippable compositions.
- [#289](https://github.com/bumble-tech/appyx/issues/289) – **Added**: Introduced `interop-rx3` for RxJava 3 support. This has identical functionality to `interop-rx2`.
Expand Down
9 changes: 5 additions & 4 deletions documentation/navmodel/backstack.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ The back stack also supports different back press and operation strategies (see
## States

```kotlin
enum class State {
@Parcelize
enum class State : Parcelable {
CREATED, ACTIVE, STASHED, DESTROYED,
}
```
Expand All @@ -33,7 +34,7 @@ Check out the apps in our [Coding challenges](../how-to-use-appyx/coding-challen
As the back stack can never be empty, it's required to define an initial target.

```kotlin
class BackStack<NavTarget : Any>(
class BackStack<NavTarget : Parcelable>(
initialElement: NavTarget,
savedStateMap: SavedStateMap?,
// Optional parameters are omitted
Expand Down Expand Up @@ -131,7 +132,7 @@ Effect on stack: depends on the contents of the stack:
You can override the default strategy in the constructor. You're not limited to using the provided classes, feel free to implement your own.

```kotlin
class BackStack<NavTarget : Any>(
class BackStack<NavTarget : Parcelable>(
/* ... */
backPressHandler: BackPressHandlerStrategy<NavTarget, State> = PopBackPressHandler(),
/* ... */
Expand All @@ -152,7 +153,7 @@ Serves as a no-op.
You can override the default strategy in the constructor. You're not limited to using the provided classes, feel free to implement your own.

```kotlin
class BackStack<NavTarget : Any>(
class BackStack<NavTarget : Parcelable>(
/* ... */
operationStrategy: OperationStrategy<NavTarget, State> = ExecuteImmediately(),
/* ... */
Expand Down
11 changes: 9 additions & 2 deletions documentation/navmodel/cards.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@ The `Cards` NavModel is not currently published, however you can try it in `:sam
## States

```kotlin
sealed class State {
sealed class State: Parcelable {
@Parcelize
data class Queued(val queueNumber: Int) : State()
@Parcelize
object Bottom : State()
@Parcelize
object Top : State()
@Parcelize
object IndicateLike : State()
@Parcelize
object IndicatePass : State()
@Parcelize
object VoteLike : State()
@Parcelize
object VotePass : State()
}
```
Expand All @@ -35,7 +42,7 @@ sealed class State {
Requires defining items that will be converted to profile cards. The first one in the list will become a `Top` card, the second one a `Bottom` card, the rest will be `Queued`.

```kotlin
class Cards<NavTarget : Any>(
class Cards<NavTarget : Parcelable>(
initialItems: List<NavTarget> = listOf(),
) : BaseNavModel<NavTarget, State>(
screenResolver = CardsOnScreenResolver,
Expand Down
9 changes: 5 additions & 4 deletions documentation/navmodel/custom.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ A step-by-step guide. You can also take a look at other existing examples to see
Create the class; define your possible states; define your initial state.

```kotlin
class Foo<NavTarget : Any>(
class Foo<NavTarget : Parcelable>(
initialItems: List<NavTarget> = listOf(),
savedStateMap: SavedStateMap?
) : BaseNavModel<NavTarget, Foo.State>(
Expand All @@ -17,7 +17,8 @@ class Foo<NavTarget : Any>(
) {

// Your possible states for any single navigation target
enum class State {
@Parcelize
enum class State : Parcelable {
CREATED, FOO, BAR, BAZ, DESTROYED;
}

Expand Down Expand Up @@ -54,7 +55,7 @@ Define one or more operations.

```kotlin
@Parcelize
class SomeOperation<NavTarget : Any> : FooOperation<NavTarget> {
class SomeOperation<NavTarget : Parcelable> : FooOperation<NavTarget> {

override fun isApplicable(elements: FooElements<NavTarget>): Boolean =
TODO("Define whether this operation is applicable given the current state")
Expand All @@ -74,7 +75,7 @@ class SomeOperation<NavTarget : Any> : FooOperation<NavTarget> {
}

// You can add an extension method for a leaner API
fun <NavTarget : Any> Foo<NavTarget>.someOperation() {
fun <NavTarget : Parcelable> Foo<NavTarget>.someOperation() {
accept(FooOperation())
}
```
Expand Down
3 changes: 2 additions & 1 deletion documentation/navmodel/promoter.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ If you feel that this functionality should be part of the main library, please l
## States

```kotlin
enum class State {
@Parcelize
enum class State : Parcelable {
CREATED, STAGE1, STAGE2, STAGE3, STAGE4, SELECTED, DESTROYED
}
```
Expand Down
9 changes: 5 additions & 4 deletions documentation/navmodel/spotlight.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ It's great for flows or tabbed containers.
## States

```kotlin
enum class State {
@Parcelize
enum class State : Parcelable {
INACTIVE_BEFORE, ACTIVE, INACTIVE_AFTER;
}
```
Expand All @@ -19,7 +20,7 @@ enum class State {
Requires defining items and an active index.

```kotlin
class Spotlight<NavTarget : Any>(
class Spotlight<NavTarget : Parcelable>(
items: List<NavTarget>,
initialActiveIndex: Int = 0,
savedStateMap: SavedStateMap?,
Expand Down Expand Up @@ -95,7 +96,7 @@ Replaces elements held by the spotlight instance with a new list. Transitions ne
You can override the default strategy in the constructor. You're not limited to using the provided classes, feel free to implement your own.

```kotlin
class Spotlight<NavTarget : Any>(
class Spotlight<NavTarget : Parcelable>(
/* ... */
backPressHandler: BackPressHandlerStrategy<NavTarget, State> = GoToDefault(
initialActiveIndex
Expand All @@ -118,7 +119,7 @@ Runs a `Previous` operation.
You can override the default strategy in the constructor. You're not limited to using the provided classes, feel free to implement your own.

```kotlin
class Spotlight<NavTarget : Any>(
class Spotlight<NavTarget : Parcelable>(
/* ... */
operationStrategy: OperationStrategy<NavTarget, State> = ExecuteImmediately(),
/* ... */
Expand Down
3 changes: 2 additions & 1 deletion documentation/navmodel/tiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ If you feel that this functionality should be part of the main library, please l
## States

```kotlin
enum class State {
@Parcelize
enum class State : Parcelable {
CREATED, STANDARD, SELECTED, DESTROYED
}
```
Expand Down
2 changes: 1 addition & 1 deletion documentation/ui/children-view.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ override fun View(modifier: Modifier) {
}

@Composable
private fun GridExample(elements: List<NavElement<NavTarget, out Any?>>) {
private fun GridExample(elements: List<NavElement<NavTarget, out Parcelable?>>) {
LazyVerticalGrid(
columns = Fixed(2),
modifier = Modifier.fillMaxSize(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.bumble.appyx.core.AppyxTestScenario
import com.bumble.appyx.core.children.nodeOrNull
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.navigation.EmptyNavModel
import com.bumble.appyx.core.navigation.EmptyState
import kotlinx.parcelize.Parcelize
import org.junit.Assert.assertEquals
import org.junit.Rule
Expand Down Expand Up @@ -50,7 +51,7 @@ class PermanentChildTest {
buildContext: BuildContext,
) : ParentNode<TestParentNode.NavTarget>(
buildContext = buildContext,
navModel = EmptyNavModel<NavTarget, Any>(),
navModel = EmptyNavModel<NavTarget, EmptyState>(),
) {

@Parcelize
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.bumble.appyx.core.children

import android.os.Parcelable
import com.bumble.appyx.core.navigation.NavKey
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.state.SavedStateMap

// custom equals/hashCode for MutableStateFlow and equality checks
sealed class ChildEntry<T> {
sealed class ChildEntry<T : Parcelable> {
abstract val key: NavKey<T>

override fun equals(other: Any?): Boolean =
Expand All @@ -18,12 +19,12 @@ sealed class ChildEntry<T> {
"$key@${javaClass.simpleName}"

/** All public APIs should return this type of child which is ready to work with. */
class Initialized<T>(
class Initialized<T : Parcelable>(
override val key: NavKey<T>,
val node: Node,
) : ChildEntry<T>()

class Suspended<T>(
class Suspended<T : Parcelable>(
override val key: NavKey<T>,
val savedState: SavedStateMap?,
) : ChildEntry<T>()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.bumble.appyx.core.children

import android.os.Parcelable
import com.bumble.appyx.core.node.Node

val <T> ChildEntry<T>.nodeOrNull: Node?
val <T : Parcelable> ChildEntry<T>.nodeOrNull: Node?
get() =
when (this) {
is ChildEntry.Initialized -> node
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bumble.appyx.core.children

import android.os.Parcelable
import androidx.lifecycle.coroutineScope
import com.bumble.appyx.core.modality.AncestryInfo
import com.bumble.appyx.core.modality.BuildContext
Expand All @@ -21,7 +22,7 @@ import kotlinx.coroutines.launch
*
* Lifecycle of these nodes is managed in [com.bumble.appyx.core.lifecycle.ChildNodeLifecycleManager].
*/
internal class ChildNodeCreationManager<NavTarget : Any>(
internal class ChildNodeCreationManager<NavTarget : Parcelable>(
private var savedStateMap: SavedStateMap?,
private val customisations: NodeCustomisationDirectory,
private val keepMode: ChildEntry.KeepMode,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bumble.appyx.core.composable

import android.os.Parcelable
import androidx.compose.animation.core.Transition
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
Expand Down Expand Up @@ -32,7 +33,7 @@ import com.bumble.appyx.core.navigation.transition.TransitionParams
import kotlinx.coroutines.flow.map

@Composable
fun <NavTarget : Any, State> ParentNode<NavTarget>.Child(
fun <NavTarget : Parcelable, State : Parcelable> ParentNode<NavTarget>.Child(
navElement: NavElement<NavTarget, out State>,
saveableStateHolder: SaveableStateHolder,
transitionParams: TransitionParams,
Expand Down Expand Up @@ -87,7 +88,7 @@ private class ChildRendererImpl(
}

@Composable
fun <NavTarget : Any, State> ParentNode<NavTarget>.Child(
fun <NavTarget : Parcelable, State : Parcelable> ParentNode<NavTarget>.Child(
navElement: NavElement<NavTarget, out State>,
modifier: Modifier = Modifier,
transitionHandler: TransitionHandler<NavTarget, State> = JumpToEndTransitionHandler(),
Expand Down Expand Up @@ -125,7 +126,7 @@ fun <NavTarget : Any, State> ParentNode<NavTarget>.Child(
}
}

private fun <NavTarget : Any, State> NavElement<NavTarget, State>.createDescriptor(
private fun <NavTarget : Parcelable, State : Parcelable> NavElement<NavTarget, State>.createDescriptor(
transitionParams: TransitionParams
) =
TransitionDescriptor(
Expand All @@ -137,15 +138,15 @@ private fun <NavTarget : Any, State> NavElement<NavTarget, State>.createDescript
)

@Composable
fun <R, S> NavModel<R, S>?.childrenAsState(): State<NavElements<R, out S>> =
fun <R : Parcelable, S : Parcelable> NavModel<R, S>?.childrenAsState(): State<NavElements<R, out S>> =
if (this != null) {
elements.collectAsState()
} else {
remember { mutableStateOf(emptyList()) }
}

@Composable
fun <R, S> NavModel<R, S>?.visibleChildrenAsState(): State<NavElements<R, out S>> =
fun <R : Parcelable, S : Parcelable> NavModel<R, S>?.visibleChildrenAsState(): State<NavElements<R, out S>> =
if (this != null) {
val visibleElementsFlow = remember { screenState.map { it.onScreen } }
visibleElementsFlow.collectAsState(emptyList())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bumble.appyx.core.composable

import android.os.Parcelable
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
Expand All @@ -26,7 +27,7 @@ import kotlinx.coroutines.flow.map
import kotlin.reflect.KClass

@Composable
inline fun <reified NavTarget : Any, State> ParentNode<NavTarget>.Children(
inline fun <reified NavTarget : Parcelable, State : Parcelable> ParentNode<NavTarget>.Children(
navModel: NavModel<NavTarget, State>,
modifier: Modifier = Modifier,
transitionHandler: TransitionHandler<NavTarget, State> = JumpToEndTransitionHandler(),
Expand Down Expand Up @@ -63,7 +64,7 @@ inline fun <reified NavTarget : Any, State> ParentNode<NavTarget>.Children(
}
}

class ChildrenTransitionScope<T : Any, S>(
class ChildrenTransitionScope<T : Parcelable, S : Parcelable>(
private val transitionHandler: TransitionHandler<T, S>,
private val transitionParams: TransitionParams,
private val navModel: NavModel<T, S>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bumble.appyx.core.lifecycle

import android.os.Parcelable
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleRegistry
import com.bumble.appyx.core.children.ChildEntry
Expand All @@ -23,7 +24,7 @@ import kotlinx.coroutines.launch
* Hosts [LifecycleRegistry] to manage the current node lifecycle
* and updates lifecycle of children nodes when updated.
*/
internal class ChildNodeLifecycleManager<NavTarget>(
internal class ChildNodeLifecycleManager<NavTarget : Parcelable>(
private val navModel: NavModel<NavTarget, *>,
private val children: StateFlow<ChildEntryMap<NavTarget>>,
private val coroutineScope: CoroutineScope,
Expand Down Expand Up @@ -86,7 +87,7 @@ internal class ChildNodeLifecycleManager<NavTarget>(
}
}

private fun <NavTarget> Flow<NavModelAdapter.ScreenState<NavTarget, *>>.keys() =
private fun <NavTarget : Parcelable> Flow<NavModelAdapter.ScreenState<NavTarget, *>>.keys() =
this
.map { screenState ->
ScreenState(
Expand All @@ -100,7 +101,7 @@ internal class ChildNodeLifecycleManager<NavTarget>(
nodeOrNull?.updateLifecycleState(state)
}

private data class ScreenState<NavTarget>(
private data class ScreenState<NavTarget : Parcelable>(
val onScreen: List<NavKey<NavTarget>> = emptyList(),
val offScreen: List<NavKey<NavTarget>> = emptyList(),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bumble.appyx.core.navigation

import android.os.Parcelable
import androidx.activity.OnBackPressedCallback
import com.bumble.appyx.core.mapState
import com.bumble.appyx.core.navigation.backpresshandlerstrategies.BackPressHandlerStrategy
Expand Down Expand Up @@ -28,7 +29,7 @@ import kotlin.coroutines.EmptyCoroutineContext
*
* If more granular configuration is required, you should inherit NavModel interface instead.
*/
abstract class BaseNavModel<NavTarget, State>(
abstract class BaseNavModel<NavTarget : Parcelable, State : Parcelable>(
private val backPressHandler: BackPressHandlerStrategy<NavTarget, State> = DontHandleBackPress(),
private val operationStrategy: OperationStrategy<NavTarget, State> = ExecuteImmediately(),
private val screenResolver: OnScreenStateResolver<State>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.bumble.appyx.core.navigation

import android.os.Parcelable
import com.bumble.appyx.core.navigation.onscreen.OnScreenStateResolver

/**
* An implementation of a NavModel that won't add any children.
* This is potentially useful if your ParentNode only uses
* [com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel]
*/
class EmptyNavModel<NavTarget, State> : BaseNavModel<NavTarget, State>(
class EmptyNavModel<NavTarget: Parcelable, State: Parcelable> : BaseNavModel<NavTarget, State>(
savedStateMap = null,
finalState = null,
screenResolver = OnScreenStateResolver { true }
Expand Down
Loading