Code Monkey home page Code Monkey logo

bumble-tech / appyx Goto Github PK

View Code? Open in Web Editor NEW
1.1K 12.0 60.0 53.27 MB

Model-driven navigation + UI components with gesture control for Compose Multiplatform

Home Page: https://bumble-tech.github.io/appyx/

License: Apache License 2.0

Kotlin 96.43% HTML 0.08% CSS 0.03% Python 0.27% Shell 0.04% JavaScript 3.15%
android android-architecture android-library jetpack-compose jetpack-compose-animation jetpack-compose-navigation compose-multiplatform gesture-control gestures transition-animation

appyx's People

Contributors

antonshilov avatar cherryperry avatar gaelmarhic avatar jlmd avatar kovalevandrey avatar lachlanmckee avatar manuel-martos avatar mapm14 avatar milospopovicbumble avatar mmartosdev avatar ninjars avatar rationalrank avatar rhoadster91 avatar samups avatar sfeatherstone avatar simon-featherstone avatar varanatol avatar vladcipariu91 avatar waveduke avatar zsoltk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

appyx's Issues

Backstack: triggering navigation mid-transition discards previous transition

The issue seems to be happening when doing consecutive operations (push or pop), best explained with video:

appyx-backstack-issue-rec.mp4

Full sample code:

class RootActivity : NodeActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    WindowCompat.setDecorFitsSystemWindows(window, false)

    setContent {
      MaterialTheme {
        NodeHost(
          integrationPoint = integrationPoint,
          factory = { buildContext ->
            RootNode(
              buildContext = buildContext,
              backstack = BackStack(
                initialElement = RootNode.Routing.Child("Root"),
                savedStateMap = buildContext.savedStateMap,
              )
            )
          },
        )
      }
    }
  }
}

class RootNode(
  buildContext: BuildContext,
  private val backstack: BackStack<Routing>,
) : ParentNode<RootNode.Routing>(
  routingSource = backstack,
  buildContext = buildContext,
) {
  sealed class Routing : Parcelable {
    @Parcelize
    data class Child(val id: String) : Routing()
  }

  @Composable
  override fun View(modifier: Modifier) {
    Surface(
      modifier = modifier.fillMaxSize(),
      color = Color.White
    ) {
      Children(
        routingSource = backstack,
        transitionHandler = rememberBackstackSlider(
          transitionSpec = { tween(2000, easing = LinearEasing) }
        ),
      )
    }
  }

  override fun resolve(routing: Routing, buildContext: BuildContext): Node {
    return ChildNode(
      name = when (routing) {
        is Routing.Child -> routing.id
      },
      buildContext = buildContext,
      pushNew = { backstack.push(Routing.Child(UUID.randomUUID().toString())) },
    )
  }
}

class ChildNode(
  private val name: String,
  private val pushNew: () -> Unit,
  buildContext: BuildContext
) : Node(buildContext = buildContext) {

  private val colors = listOf(
    manatee,
    sizzling_red,
    atomic_tangerine,
    silver_sand,
    md_pink_500,
    md_indigo_500,
    md_blue_500,
    md_light_blue_500,
    md_cyan_500,
    md_teal_500,
    md_light_green_500,
    md_lime_500,
    md_amber_500,
    md_grey_500,
    md_blue_grey_500
  )

  private val colorIndex =
    buildContext.savedStateMap?.get(KEY_COLOR_INDEX) as? Int ?: Random.nextInt(colors.size)
  private val color = colors[colorIndex]

  override fun onSaveInstanceState(state: MutableSavedStateMap) {
    super.onSaveInstanceState(state)
    state[KEY_COLOR_INDEX] = colorIndex
  }

  @Composable
  override fun View(modifier: Modifier) {
    Box(
      modifier = Modifier
        .fillMaxSize()
        .background(
          color = color,
          shape = RoundedCornerShape(6.dp)
        )
    ) {
      Column(
        modifier = Modifier.padding(24.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp),
      ) {
        Text("Child ($name).")
        Row {
          // Local UI state should be saved too (both in backstack and onSaveInstanceState)
          var counter by rememberSaveable { mutableStateOf(0) }
          Text(text = "Counter $counter", modifier = Modifier.align(CenterVertically))
          Spacer(modifier = Modifier.width(16.dp))
          Button(onClick = { counter++ }, content = { Text("Increment") })
        }
        Row {
          Button(onClick = { navigateUp() }, content = { Text("Go up") })
        }
        Row {
          Button(onClick = pushNew, content = { Text("Push new") })
        }
      }
    }
  }

  companion object {
    private const val KEY_COLOR_INDEX = "ColorIndex"
  }
}

Add gradle cache to github actions

This will make Github Action jobs run faster as long as no changes have been made to the gradle wrapper, build.settings/settings.gradle or libs.versions.toml

IntegrationPointExample does not work with "do not keep activities"

Steps:

  1. Do not keep activities
  2. Open IntegrationPointExample
  3. Launch activity for result
  4. Return random result code
  5. Node has not received it

Issue:

  1. The node is created and subscribed to ActivityStarter
  2. When the next activity is launched the node is destroyed
  3. onActivityResult is invoked and events are pushed
  4. Compose rendering is triggered and the node is recreated (Activity.setContent { })
  5. The node is created and subscribed to ActivityStarter

Discovered after converting error log into exception in #173

testing-ui module code is currently being included in sandbox release build

The testing-ui module is currently being defined as an implementation as well as an androidTestImplementation dependency. When I attempted to remove the implementation usage, the tests started failing.

This is because the testing-ui module includes an activity. This activity must be registered within the main apps manifest. This would then require developers to do something like debugImplementation testing-ui which then leaks test code into the debug build.

To fix this, we should create a testing-ui-activity module which only defines the activity. This allows the tests to keep working, and also avoids adding espresso code in a debug build.

This would like like:
debugImplementation testing-ui-activity
androidTestImplementation testing-ui

See Google's own documentation that shows that this is necessary: https://developer.android.com/jetpack/compose/testing#setup

I tried to get the activity working without adding it into the main apk, and it doesn't seem to work.

ParentNodeView potential recompositions

Please investigate if it is an issue.

class ParentNodeView<...> {

  @Composable
  abstract fun ParentNode<Routing>.NodeView(modifier: Modifier)

}

abstract fun ParentNode<Routing>.NodeView(modifier: Modifier) is equal to abstract fun NodeView(node: ParentNode<Routing>, modifier: Modifier).

ParentNode is not marked as @Stable so I suspect that we might have unnecessary recompositions in this case.

Spotlight crashing when swiftly selecting next element while previous transition is running

Steps to reproduce:

  1. Add a few elements to Spotlight
  2. Swiftly invoke next action a few times without waiting for the transitions to finish
  3. See the crash when you reach the end of the list:

Stacktrace:

 java.util.NoSuchElementException: Collection contains no element matching the predicate.
        at com.bumble.appyx.routingsource.spotlight.operation.Next.invoke(Next.kt:52)
        at com.bumble.appyx.routingsource.spotlight.operation.Next.invoke(Next.kt:11)
        at com.bumble.appyx.core.routing.BaseRoutingSource$execute$1.invoke(BaseRoutingSource.kt:114)
        at com.bumble.appyx.core.routing.BaseRoutingSource$execute$1.invoke(BaseRoutingSource.kt:112)
        at com.bumble.appyx.core.routing.BaseRoutingSource.updateState(BaseRoutingSource.kt:106)
        at com.bumble.appyx.core.routing.BaseRoutingSource.execute(BaseRoutingSource.kt:112)
        at com.bumble.appyx.core.routing.BaseRoutingSource.access$execute(BaseRoutingSource.kt:33)
        at com.bumble.appyx.core.routing.BaseRoutingSource$1.invoke(BaseRoutingSource.kt:97)
        at com.bumble.appyx.core.routing.BaseRoutingSource$1.invoke(BaseRoutingSource.kt:97)
        at com.bumble.appyx.core.routing.operationstrategies.ExecuteImmediately.accept(ExecuteImmediately.kt:8)
        at com.bumble.appyx.core.routing.BaseRoutingSource.accept(BaseRoutingSource.kt:101)
        at com.bumble.appyx.routingsource.spotlight.operation.NextKt.next(Next.kt:44)
        at com.bumble.appyx.sandbox.client.spotlight.SpotlightExampleNode$LoadedState$1$2$1$1.invoke(SpotlightExampleNode.kt:160)
        at com.bumble.appyx.sandbox.client.spotlight.SpotlightExampleNode$LoadedState$1$2$1$1.invoke(SpotlightExampleNode.kt:159)

Move appcompat code out of core module

The core module currently depends on the Android appcompat library using implementation. Rather than exposing it as api it might be worthwhile to extract this into core-appcompat, as this will give developers a choice (as they may not be allowed to use the appcompat library)

Add testing section to documentation

Now that we have a testing utils for both UI and unit tests, we should update the documentation to show a brief example of how to write a test.

We might want to separate them into two different pages, and the navigation would look similar to

Testing

UI testing
Unit testing

Fix sandbox on small screen devices

On small screen devices (you can reproduce by using split screen if required) several of the lists within the sandbox do not scroll.

This causes the content to become truncated. We should add the vertical scrolling modifier to fix these Nodes.

Consider introducing a Github merge queue

Currently all branches are merged into main without requiring being up to date with main.

This could mean we merge an old branch that has a breaking change, and the main branch needs to be fixed.

Also, potentially if we introduce stricter static code analysis, potentially this older branch will increase our tech debt

We should consider using a merge queue to avoid this problem: https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue

Otherwise we could also enforce that all PRs need to be up to date with main, but I believe that will be far more annoying.

Convert 'customisations' module from Android to Java

The 'customisations' does not need to be an Android module, and should be converted to a Java module. Should we wish to support multiplatform in the future we will need these modules to not be Android specific

Crash when activity is recreated

Steps to reproduce:

  1. Enable live literals
  2. Change any text
  3. See the crash with stack trace:
Process: com.bumble.appyx, PID: 9242
java.lang.IllegalStateException: A root has already been attached to this integration point
	at com.bumble.appyx.core.integrationpoint.IntegrationPoint.attach(IntegrationPoint.kt:27)
	at com.bumble.appyx.core.integration.NodeHostKt$NodeHost$1$1.invokeSuspend(NodeHost.kt:40)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch(AndroidUiDispatcher.android.kt:81)
	at androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch(AndroidUiDispatcher.android.kt:41)
	at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:68)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1035)
	at android.view.Choreographer.doCallbacks(Choreographer.java:845)
	at android.view.Choreographer.doFrame(Choreographer.java:775)
	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1022)
	at android.os.Handler.handleCallback(Handler.java:938)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loopOnce(Looper.java:201)
	at android.os.Looper.loop(Looper.java:288)
	at android.app.ActivityThread.main(ActivityThread.java:7870)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.ui.platform.MotionDurationScaleImpl@766958, androidx.compose.runtime.BroadcastFrameClock@893dab1, StandaloneCoroutine{Cancelling}@97c3b96, AndroidUiDispatcher@114317]

Remove FragmentIntegrationPoint

FragmentIntegrationPoint is no longer needed as we should use ActivityIntegrationPoint.getIntegrationPoint(context: Context) method.

Create publishing plugin

Rather than using the existing publication.gradle class to setup our publication, we should use a plugin instead as this will give us greater type safety when switching to Kotlin gradle files (see #49)

Support ComponentActivity for Jetpack Compose projects

NodeActivity extends AppCompatActivity, whereas ComponentActivity used in Jetpack Compose template projects does not. This impedes adoption of Appyx in new projects.

Furthermore, ActivityIntegrationPoint (the integration with which is the sole purpose of NodeActivity) requires AppCompatActivity as a parameter, preventing a user simply creating their own wrapper.

It looks like ActivityIntegrationPoint could just take Activity as its first parameter, making it more re-usable, and it should also be possible to provide a convenient boilerplate equivalent for NodeActivity that extends ComponentActivity, for use in Jetpack Compose projects.

Setup detekt

Setup detekt + report issues to CodeQL like here #63 using SARIF file support in detekt

Interop Integrationpoint shouldn't force to expose appyx interface

Current interop integration point required activities to implement IntegrationPointAppyxProvider.

This expose the interface in module edges, forcing parent modules to implement appyx event if they are not using it.

Example.

Module A -> Module B

Module B implements SomeActiviy: RibAcitivty(), IntegrationPointAppyxProvider{...}
Module A has no appyx dependencies.

Compilation failed as Module A will try to resolve IntegrationPointAppyxProvider

onBackPressed memory leak

When I quit my app using the back button on a phone running SDK 29, Leak Canary reports a memory leak.

If I use the fix reported here: https://issuetracker.google.com/issues/139738913, the leak is fixed.

override fun onBackPressed() {
        // Fix for Memory Leak: https://issuetracker.google.com/issues/139738913
        if (Build.VERSION.SDK_INT == 29 && !onBackPressedDispatcher.hasEnabledCallbacks()) {
            finishAfterTransition()
        } else {
            super.onBackPressed()
        }
    }

However, if I use Appyx NodeActivity with a RootNode, I get the memory leak again.

Add AndroidTest execution to Github actions

Currently it is possible for someone to merge to main without running the AndroidTests within the Sandbox module (and potentially future modules). This could mean that the tests will break silently.

Introduce automatic dependency update tool

There are quite a few libraries in the project that are out of date. Rather than having to remember to check these occasionally (and potentially wait months), we should investigate using a tool that does this automatically.

Unfortunately dependabot does not work with Gradle Version Catalogues (link), so we should use a different solution for now.

It looks like square is using 'Renovate' (example) which looks like it works with version catalogues. This tool potentially needs to be installed as an app, though I did see something about being able to 'self-host' and use a Github action.

This tool will automatically raise PRs for us when a new dependency is available, and will help us avoid being very out of date.

Verify API stability after release

For all published modules:

  • Use org.jetbrains.kotlinx:binary-compatibility-validator and add it as part of CI workflow
  • Use explicitApi() mode for Kotlin Gradle plugin

Create ParentNodeViewBase as interface to allow customisations in builders become more flexible

Views are difficult to subistute when ParentNodeView<> is an abstract class. Making ParentNodeView<> an interface would fix this.

Case 1
In a Builder class you are expected to pass the view as ObservableSource<Event>, Consumer<ViewModel> to the Interactor and as ParentNodeView<> to the Node. If I want optionally pass in the view when created as a testable mock from a test build I can't without sharing the concrete type.

Case 2
View Customisations

The example for Customisations has the factory returning a NodeView. This would not satisfy the requirements above for a builder. Again to abstract this I would want to create a interface that has ObservableSource<Event>, Consumer<ViewModel> and ParentNodeView<> as an interface.

Sandbox IntegrationPointExampleNode throws exception when Activity recreated

The Sandbox IntegrationPointExampleNode class throws an exception when Activity recreated due to the integrationPoint not being attached yet.

We should change the node to not access the integrationPoint immediately, and also update the error message for clarity.

Demontration
Using the dark mode button to cause the Activity to be recreated.

appyx-sandbox-lifecycle-crash.webm

Stacktrace

java.lang.IllegalStateException: You're accessing an IntegrationPointStub. This means you're using a Node without ever integrating it to a proper IntegrationPoint. This is fine during tests with limited scope, but it looks like the code that leads here requires interfacing with a valid implementation.
	at com.bumble.appyx.core.integrationpoint.IntegrationPointStub.getPermissionRequester(IntegrationPointStub.kt:18)
	at com.bumble.appyx.sandbox.client.integrationpoint.IntegrationPointExampleNode.<init>(IntegrationPointExampleNode.kt:37)
	at com.bumble.appyx.sandbox.client.container.ContainerNode.resolve(ContainerNode.kt:130)
	at com.bumble.appyx.sandbox.client.container.ContainerNode.resolve(ContainerNode.kt:63)
	at com.bumble.appyx.core.children.ChildEntry$Companion.create(ChildEntry.kt:58)
	at com.bumble.appyx.core.node.ParentNode.restoreChildren(ParentNode.kt:121)
	at com.bumble.appyx.core.node.ParentNode.onBuilt(ParentNode.kt:82)
	at com.bumble.appyx.core.node.NodeExtKt.build(NodeExt.kt:3)
	at com.bumble.appyx.core.integration.NodeHostKt$rememberNode$2.invoke(NodeHost.kt:72)
	at com.bumble.appyx.core.integration.NodeHostKt$rememberNode$2.invoke(NodeHost.kt:64)
	at androidx.compose.runtime.saveable.MapSaverKt$mapSaver$2.invoke(MapSaver.kt:52)
	at androidx.compose.runtime.saveable.MapSaverKt$mapSaver$2.invoke(MapSaver.kt:33)
	at androidx.compose.runtime.saveable.SaverKt$Saver$1.restore(Saver.kt:68)
	at androidx.compose.runtime.saveable.RememberSaveableKt$mutableStateSaver$1$2.invoke(RememberSaveable.kt:162)
	at androidx.compose.runtime.saveable.RememberSaveableKt$mutableStateSaver$1$2.invoke(RememberSaveable.kt:151)
	at androidx.compose.runtime.saveable.SaverKt$Saver$1.restore(Saver.kt:68)
	at androidx.compose.runtime.saveable.RememberSaveableKt.rememberSaveable(RememberSaveable.kt:86)
	at androidx.compose.runtime.saveable.RememberSaveableKt.rememberSaveable(RememberSaveable.kt:142)
	at com.bumble.appyx.core.integration.NodeHostKt.rememberNode(NodeHost.kt:62)
	at com.bumble.appyx.core.integration.NodeHostKt.NodeHost(NodeHost.kt:38)
	at com.bumble.appyx.sandbox.MainActivity$onCreate$1$1$1.invoke(MainActivity.kt:25)
	at com.bumble.appyx.sandbox.MainActivity$onCreate$1$1$1.invoke(MainActivity.kt:23)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.material.SurfaceKt$Surface$1.invoke(Surface.kt:134)
	at androidx.compose.material.SurfaceKt$Surface$1.invoke(Surface.kt:117)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
	at androidx.compose.material.SurfaceKt.Surface-F-jzlyU(Surface.kt:114)
	at com.bumble.appyx.sandbox.MainActivity$onCreate$1$1.invoke(MainActivity.kt:23)
	at com.bumble.appyx.sandbox.MainActivity$onCreate$1$1.invoke(MainActivity.kt:21)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.material.MaterialTheme_androidKt.PlatformMaterialTheme(MaterialTheme.android.kt:23)
	at androidx.compose.material.MaterialThemeKt$MaterialTheme$1$1.invoke(MaterialTheme.kt:82)
	at androidx.compose.material.MaterialThemeKt$MaterialTheme$1$1.invoke(MaterialTheme.kt:81)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
	at androidx.compose.material.TextKt.ProvideTextStyle(Text.kt:265)
	at androidx.compose.material.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:81)
	at androidx.compose.material.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:80)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
	at androidx.compose.material.MaterialThemeKt.MaterialTheme(MaterialTheme.kt:72)
	at com.bumble.appyx.sandbox.ui.ThemeKt.AppyxSandboxTheme(Theme.kt:39)
	at com.bumble.appyx.sandbox.MainActivity$onCreate$1.invoke(MainActivity.kt:21)
	at com.bumble.appyx.sandbox.MainActivity$onCreate$1.invoke(MainActivity.kt:20)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.ui.platform.ComposeView.Content(ComposeView.android.kt:402)
	at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:248)
	at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:247)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
	at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:177)
	at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:123)
	at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:122)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
	at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:114)
	at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:157)
	at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$3.invoke(Wrapper.android.kt:156)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
	at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:156)
	at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:140)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:74)
	at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3193)
	at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3183)
	at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:252)
	at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(Unknown Source:1)
	at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3183)
	at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3119)
	at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:578)
	at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:811)
	at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:513)
	at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:140)
	at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:131)
	at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:1015)
	at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:131)
	at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:182)
	at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:354)
	at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:196)
	at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:138)
	at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:131)
	at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.android.kt:1102)
	at android.view.View.dispatchAttachedToWindow(View.java:20479)
	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3489)
	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3496)
	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3496)
	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3496)
	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3496)
	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3496)
	at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3496)
	at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2417)
	at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1952)
	at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8171)
	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
	at android.view.Choreographer.doCallbacks(Choreographer.java:796)
	at android.view.Choreographer.doFrame(Choreographer.java:731)
	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
	at android.os.Handler.handleCallback(Handler.java:938)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loop(Looper.java:223)
	at android.app.ActivityThread.main(ActivityThread.java:7656)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

Nested `BackPressHandler`s are ignored after app goes to background

Haven't investigated the specifics, but here's the gist:

  • Have a ParentNode with BackStack or Modals that has a leaf ParentNode that also has BackStack
  • Push node to a nested BackStack
  • Put the app in background
  • Open the app
  • Only the root BackPressHandler is called

Can be reproduced in sandbox app in "Combined routing source" or "Modal example" or "Spotlight example"

Edit: Looks like this would be fixed by #32 ๐Ÿค”

Convert gradle files to kotlin language

Should we decide to support Kotlin Multiplatform in the future it would be easier for us if we supported the kotlin version of gradle build files. From what I have seen, most examples use build.gradle.kts, and it could be difficult (or impossible) to do certain things with the Groovy API.

For example the val androidMain by getting found here

Make NodeView interface stable

Generate compiler report and see that NodeView and its implementations are used as this parameter for composable functions. It's safe to mark it as stable improving performance

Back press handling after fold/unfold

Looks like there is slightly more to this โ€“ now a OnBackPressedCallback from a custom BackPressHandler plugin doesn't get invoked after onStop

Easiest way to repro this is in "Blocker" sample in sandbox, with same steps as before

Originally posted by @steelahhh in #102 (comment)

Support integration point for multiple roots

At the moment IntegrationPoint is designed to have only one root in Appyx tree and will crash if we try to attach another one. In reality there can be more than one Appyx tree (similar to composition). For instance, clients may want to use Appyx in their codebase in disconnected from each other places creating more than one tree

Improve InteropView error messaging

At the moment the InteropView error messaging (Activity where InteropNode is used must implement IntegrationPointAppyxProvider) makes it difficult to tell which activity and which node have caused the issue.

By adding the class names to the error message, we can make investigating this problem easier.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.