Skip to content

Commit 9806dbe

Browse files
committed
[bumble-tech#310] Ensure NavTarget and State are parcelable
1 parent 4f6678e commit 9806dbe

File tree

150 files changed

+581
-335
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

150 files changed

+581
-335
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Pending changes
44

5+
- [#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.
56
- [#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.
67
- [#287](https://github.com/bumble-tech/appyx/pull/287)**Added**: `ImmutableList` has been added to avoid non-skippable compositions.
78
- [#289](https://github.com/bumble-tech/appyx/issues/289)**Added**: Introduced `interop-rx3` for RxJava 3 support. This has identical functionality to `interop-rx2`.

documentation/navmodel/backstack.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ The back stack also supports different back press and operation strategies (see
1616
## States
1717

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

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

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

154155
```kotlin
155-
class BackStack<NavTarget : Any>(
156+
class BackStack<NavTarget : Parcelable>(
156157
/* ... */
157158
operationStrategy: OperationStrategy<NavTarget, State> = ExecuteImmediately(),
158159
/* ... */

documentation/navmodel/cards.md

+9-2
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,20 @@ The `Cards` NavModel is not currently published, however you can try it in `:sam
1515
## States
1616

1717
```kotlin
18-
sealed class State {
18+
sealed class State: Parcelable {
19+
@Parcelize
1920
data class Queued(val queueNumber: Int) : State()
21+
@Parcelize
2022
object Bottom : State()
23+
@Parcelize
2124
object Top : State()
25+
@Parcelize
2226
object IndicateLike : State()
27+
@Parcelize
2328
object IndicatePass : State()
29+
@Parcelize
2430
object VoteLike : State()
31+
@Parcelize
2532
object VotePass : State()
2633
}
2734
```
@@ -35,7 +42,7 @@ sealed class State {
3542
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`.
3643

3744
```kotlin
38-
class Cards<NavTarget : Any>(
45+
class Cards<NavTarget : Parcelable>(
3946
initialItems: List<NavTarget> = listOf(),
4047
) : BaseNavModel<NavTarget, State>(
4148
screenResolver = CardsOnScreenResolver,

documentation/navmodel/custom.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ A step-by-step guide. You can also take a look at other existing examples to see
77
Create the class; define your possible states; define your initial state.
88

99
```kotlin
10-
class Foo<NavTarget : Any>(
10+
class Foo<NavTarget : Parcelable>(
1111
initialItems: List<NavTarget> = listOf(),
1212
savedStateMap: SavedStateMap?
1313
) : BaseNavModel<NavTarget, Foo.State>(
@@ -17,7 +17,8 @@ class Foo<NavTarget : Any>(
1717
) {
1818

1919
// Your possible states for any single navigation target
20-
enum class State {
20+
@Parcelize
21+
enum class State : Parcelable {
2122
CREATED, FOO, BAR, BAZ, DESTROYED;
2223
}
2324

@@ -54,7 +55,7 @@ Define one or more operations.
5455

5556
```kotlin
5657
@Parcelize
57-
class SomeOperation<NavTarget : Any> : FooOperation<NavTarget> {
58+
class SomeOperation<NavTarget : Parcelable> : FooOperation<NavTarget> {
5859

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

7677
// You can add an extension method for a leaner API
77-
fun <NavTarget : Any> Foo<NavTarget>.someOperation() {
78+
fun <NavTarget : Parcelable> Foo<NavTarget>.someOperation() {
7879
accept(FooOperation())
7980
}
8081
```

documentation/navmodel/promoter.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ If you feel that this functionality should be part of the main library, please l
1212
## States
1313

1414
```kotlin
15-
enum class State {
15+
@Parcelize
16+
enum class State : Parcelable {
1617
CREATED, STAGE1, STAGE2, STAGE3, STAGE4, SELECTED, DESTROYED
1718
}
1819
```

documentation/navmodel/spotlight.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ It's great for flows or tabbed containers.
99
## States
1010

1111
```kotlin
12-
enum class State {
12+
@Parcelize
13+
enum class State : Parcelable {
1314
INACTIVE_BEFORE, ACTIVE, INACTIVE_AFTER;
1415
}
1516
```
@@ -19,7 +20,7 @@ enum class State {
1920
Requires defining items and an active index.
2021

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

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

120121
```kotlin
121-
class Spotlight<NavTarget : Any>(
122+
class Spotlight<NavTarget : Parcelable>(
122123
/* ... */
123124
operationStrategy: OperationStrategy<NavTarget, State> = ExecuteImmediately(),
124125
/* ... */

documentation/navmodel/tiles.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ If you feel that this functionality should be part of the main library, please l
1212
## States
1313

1414
```kotlin
15-
enum class State {
15+
@Parcelize
16+
enum class State : Parcelable {
1617
CREATED, STANDARD, SELECTED, DESTROYED
1718
}
1819
```

documentation/ui/children-view.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ override fun View(modifier: Modifier) {
8484
}
8585

8686
@Composable
87-
private fun GridExample(elements: List<NavElement<NavTarget, out Any?>>) {
87+
private fun GridExample(elements: List<NavElement<NavTarget, out Parcelable?>>) {
8888
LazyVerticalGrid(
8989
columns = Fixed(2),
9090
modifier = Modifier.fillMaxSize(),

libraries/core/src/androidTest/kotlin/com/bumble/appyx/core/node/PermanentChildTest.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import com.bumble.appyx.core.AppyxTestScenario
1313
import com.bumble.appyx.core.children.nodeOrNull
1414
import com.bumble.appyx.core.modality.BuildContext
1515
import com.bumble.appyx.core.navigation.EmptyNavModel
16+
import com.bumble.appyx.core.navigation.EmptyState
1617
import kotlinx.parcelize.Parcelize
1718
import org.junit.Assert.assertEquals
1819
import org.junit.Rule
@@ -50,7 +51,7 @@ class PermanentChildTest {
5051
buildContext: BuildContext,
5152
) : ParentNode<TestParentNode.NavTarget>(
5253
buildContext = buildContext,
53-
navModel = EmptyNavModel<NavTarget, Any>(),
54+
navModel = EmptyNavModel<NavTarget, EmptyState>(),
5455
) {
5556

5657
@Parcelize

libraries/core/src/main/kotlin/com/bumble/appyx/core/children/ChildEntry.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package com.bumble.appyx.core.children
22

3+
import android.os.Parcelable
34
import com.bumble.appyx.core.navigation.NavKey
45
import com.bumble.appyx.core.node.Node
56
import com.bumble.appyx.core.state.SavedStateMap
67

78
// custom equals/hashCode for MutableStateFlow and equality checks
8-
sealed class ChildEntry<T> {
9+
sealed class ChildEntry<T : Parcelable> {
910
abstract val key: NavKey<T>
1011

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

2021
/** All public APIs should return this type of child which is ready to work with. */
21-
class Initialized<T>(
22+
class Initialized<T : Parcelable>(
2223
override val key: NavKey<T>,
2324
val node: Node,
2425
) : ChildEntry<T>()
2526

26-
class Suspended<T>(
27+
class Suspended<T : Parcelable>(
2728
override val key: NavKey<T>,
2829
val savedState: SavedStateMap?,
2930
) : ChildEntry<T>()

libraries/core/src/main/kotlin/com/bumble/appyx/core/children/ChildEntryExt.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package com.bumble.appyx.core.children
22

3+
import android.os.Parcelable
34
import com.bumble.appyx.core.node.Node
45

5-
val <T> ChildEntry<T>.nodeOrNull: Node?
6+
val <T : Parcelable> ChildEntry<T>.nodeOrNull: Node?
67
get() =
78
when (this) {
89
is ChildEntry.Initialized -> node

libraries/core/src/main/kotlin/com/bumble/appyx/core/children/ChildNodeCreationManager.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.bumble.appyx.core.children
22

3+
import android.os.Parcelable
34
import androidx.lifecycle.coroutineScope
45
import com.bumble.appyx.core.modality.AncestryInfo
56
import com.bumble.appyx.core.modality.BuildContext
@@ -21,7 +22,7 @@ import kotlinx.coroutines.launch
2122
*
2223
* Lifecycle of these nodes is managed in [com.bumble.appyx.core.lifecycle.ChildNodeLifecycleManager].
2324
*/
24-
internal class ChildNodeCreationManager<NavTarget : Any>(
25+
internal class ChildNodeCreationManager<NavTarget : Parcelable>(
2526
private var savedStateMap: SavedStateMap?,
2627
private val customisations: NodeCustomisationDirectory,
2728
private val keepMode: ChildEntry.KeepMode,

libraries/core/src/main/kotlin/com/bumble/appyx/core/composable/Child.kt

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.bumble.appyx.core.composable
22

3+
import android.os.Parcelable
34
import androidx.compose.animation.core.Transition
45
import androidx.compose.foundation.layout.Box
56
import androidx.compose.runtime.Composable
@@ -32,7 +33,7 @@ import com.bumble.appyx.core.navigation.transition.TransitionParams
3233
import kotlinx.coroutines.flow.map
3334

3435
@Composable
35-
fun <NavTarget : Any, State> ParentNode<NavTarget>.Child(
36+
fun <NavTarget : Parcelable, State : Parcelable> ParentNode<NavTarget>.Child(
3637
navElement: NavElement<NavTarget, out State>,
3738
saveableStateHolder: SaveableStateHolder,
3839
transitionParams: TransitionParams,
@@ -87,7 +88,7 @@ private class ChildRendererImpl(
8788
}
8889

8990
@Composable
90-
fun <NavTarget : Any, State> ParentNode<NavTarget>.Child(
91+
fun <NavTarget : Parcelable, State : Parcelable> ParentNode<NavTarget>.Child(
9192
navElement: NavElement<NavTarget, out State>,
9293
modifier: Modifier = Modifier,
9394
transitionHandler: TransitionHandler<NavTarget, State> = JumpToEndTransitionHandler(),
@@ -125,7 +126,7 @@ fun <NavTarget : Any, State> ParentNode<NavTarget>.Child(
125126
}
126127
}
127128

128-
private fun <NavTarget : Any, State> NavElement<NavTarget, State>.createDescriptor(
129+
private fun <NavTarget : Parcelable, State : Parcelable> NavElement<NavTarget, State>.createDescriptor(
129130
transitionParams: TransitionParams
130131
) =
131132
TransitionDescriptor(
@@ -137,15 +138,15 @@ private fun <NavTarget : Any, State> NavElement<NavTarget, State>.createDescript
137138
)
138139

139140
@Composable
140-
fun <R, S> NavModel<R, S>?.childrenAsState(): State<NavElements<R, out S>> =
141+
fun <R : Parcelable, S : Parcelable> NavModel<R, S>?.childrenAsState(): State<NavElements<R, out S>> =
141142
if (this != null) {
142143
elements.collectAsState()
143144
} else {
144145
remember { mutableStateOf(emptyList()) }
145146
}
146147

147148
@Composable
148-
fun <R, S> NavModel<R, S>?.visibleChildrenAsState(): State<NavElements<R, out S>> =
149+
fun <R : Parcelable, S : Parcelable> NavModel<R, S>?.visibleChildrenAsState(): State<NavElements<R, out S>> =
149150
if (this != null) {
150151
val visibleElementsFlow = remember { screenState.map { it.onScreen } }
151152
visibleElementsFlow.collectAsState(emptyList())

libraries/core/src/main/kotlin/com/bumble/appyx/core/composable/Children.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.bumble.appyx.core.composable
22

3+
import android.os.Parcelable
34
import androidx.compose.foundation.layout.Box
45
import androidx.compose.runtime.Composable
56
import androidx.compose.runtime.collectAsState
@@ -26,7 +27,7 @@ import kotlinx.coroutines.flow.map
2627
import kotlin.reflect.KClass
2728

2829
@Composable
29-
inline fun <reified NavTarget : Any, State> ParentNode<NavTarget>.Children(
30+
inline fun <reified NavTarget : Parcelable, State : Parcelable> ParentNode<NavTarget>.Children(
3031
navModel: NavModel<NavTarget, State>,
3132
modifier: Modifier = Modifier,
3233
transitionHandler: TransitionHandler<NavTarget, State> = JumpToEndTransitionHandler(),
@@ -63,7 +64,7 @@ inline fun <reified NavTarget : Any, State> ParentNode<NavTarget>.Children(
6364
}
6465
}
6566

66-
class ChildrenTransitionScope<T : Any, S>(
67+
class ChildrenTransitionScope<T : Parcelable, S : Parcelable>(
6768
private val transitionHandler: TransitionHandler<T, S>,
6869
private val transitionParams: TransitionParams,
6970
private val navModel: NavModel<T, S>

libraries/core/src/main/kotlin/com/bumble/appyx/core/lifecycle/ChildNodeLifecycleManager.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.bumble.appyx.core.lifecycle
22

3+
import android.os.Parcelable
34
import androidx.lifecycle.Lifecycle
45
import androidx.lifecycle.LifecycleRegistry
56
import com.bumble.appyx.core.children.ChildEntry
@@ -23,7 +24,7 @@ import kotlinx.coroutines.launch
2324
* Hosts [LifecycleRegistry] to manage the current node lifecycle
2425
* and updates lifecycle of children nodes when updated.
2526
*/
26-
internal class ChildNodeLifecycleManager<NavTarget>(
27+
internal class ChildNodeLifecycleManager<NavTarget : Parcelable>(
2728
private val navModel: NavModel<NavTarget, *>,
2829
private val children: StateFlow<ChildEntryMap<NavTarget>>,
2930
private val coroutineScope: CoroutineScope,
@@ -86,7 +87,7 @@ internal class ChildNodeLifecycleManager<NavTarget>(
8687
}
8788
}
8889

89-
private fun <NavTarget> Flow<NavModelAdapter.ScreenState<NavTarget, *>>.keys() =
90+
private fun <NavTarget : Parcelable> Flow<NavModelAdapter.ScreenState<NavTarget, *>>.keys() =
9091
this
9192
.map { screenState ->
9293
ScreenState(
@@ -100,7 +101,7 @@ internal class ChildNodeLifecycleManager<NavTarget>(
100101
nodeOrNull?.updateLifecycleState(state)
101102
}
102103

103-
private data class ScreenState<NavTarget>(
104+
private data class ScreenState<NavTarget : Parcelable>(
104105
val onScreen: List<NavKey<NavTarget>> = emptyList(),
105106
val offScreen: List<NavKey<NavTarget>> = emptyList(),
106107
)

libraries/core/src/main/kotlin/com/bumble/appyx/core/navigation/BaseNavModel.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.bumble.appyx.core.navigation
22

3+
import android.os.Parcelable
34
import androidx.activity.OnBackPressedCallback
45
import com.bumble.appyx.core.mapState
56
import com.bumble.appyx.core.navigation.backpresshandlerstrategies.BackPressHandlerStrategy
@@ -28,7 +29,7 @@ import kotlin.coroutines.EmptyCoroutineContext
2829
*
2930
* If more granular configuration is required, you should inherit NavModel interface instead.
3031
*/
31-
abstract class BaseNavModel<NavTarget, State>(
32+
abstract class BaseNavModel<NavTarget : Parcelable, State : Parcelable>(
3233
private val backPressHandler: BackPressHandlerStrategy<NavTarget, State> = DontHandleBackPress(),
3334
private val operationStrategy: OperationStrategy<NavTarget, State> = ExecuteImmediately(),
3435
private val screenResolver: OnScreenStateResolver<State>,

libraries/core/src/main/kotlin/com/bumble/appyx/core/navigation/EmptyNavModel.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package com.bumble.appyx.core.navigation
22

3+
import android.os.Parcelable
34
import com.bumble.appyx.core.navigation.onscreen.OnScreenStateResolver
45

56
/**
67
* An implementation of a NavModel that won't add any children.
78
* This is potentially useful if your ParentNode only uses
89
* [com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel]
910
*/
10-
class EmptyNavModel<NavTarget, State> : BaseNavModel<NavTarget, State>(
11+
class EmptyNavModel<NavTarget: Parcelable, State: Parcelable> : BaseNavModel<NavTarget, State>(
1112
savedStateMap = null,
1213
finalState = null,
1314
screenResolver = OnScreenStateResolver { true }

0 commit comments

Comments
 (0)