Coder Social home page Coder Social logo

basic-android-kotlin-compose-training-reply-app's Introduction

Reply App - Solution Code

Solution code for the Android Basics with Compose: Reply app.

Introduction

The Reply app is a basic email client that displays various categories of your inbox. This app is used to illustrate the concept of adaptive layouts.

Pre-requisites

  • Experience with Kotlin syntax
  • How to create and run a project in Android Studio
  • How to create composable functions
  • How to create compose navigation

Getting Started

  1. Install Android Studio, if you don't already have it.
  2. Download the sample.
  3. Import the sample into Android Studio.
  4. Build and run the sample.

basic-android-kotlin-compose-training-reply-app's People

Contributors

android-dev-lxl avatar owenscott-gds 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

basic-android-kotlin-compose-training-reply-app's Issues

Build an adaptive app with adaptive layout: Android Basics with Compose

The following line breaks the Expanded Preview with an illegal cast call:

ReplyHomeContent.kt

val activity = LocalContext.current as Activity

I fixed the issue with a type check and a safe call. I believe there's a safer way to do this in a production app, but it does the job for a tutorial.

val activity: Activity? = if (LocalContext.current is Activity){
    LocalContext.current as Activity
} else {
    null
}
ReplyDetailsScreen(
    replyUiState = replyUiState,
    modifier = Modifier.weight(1f),
    onBackPressed = {activity?.finish()},
    isFullScreen = false
)

Build an adaptive app with adaptive layout: Android Basics with Compose

https://developer.android.com/codelabs/basic-android-kotlin-compose-adaptive-content-for-large-screens?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fandroid-basics-compose-unit-4-pathway-3%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fbasic-android-kotlin-compose-adaptive-content-for-large-screens#4

Step 5: Add automated test for adaptive apps

From the initial test writing, I could not get it even to run. kept getting an ActivityNotFoundException:

android.content.ActivityNotFoundException: Unable to find explicit activity class {com.example.reply.test/androidx.test.core.app.InstrumentationActivityInvoker$BootstrapActivity}; have you declared this activity in your AndroidManifest.xml, or does your intent not match its declared <intent-filter>?
at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:2197)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1839)
at androidx.test.runner.MonitoringInstrumentation.execStartActivity(MonitoringInstrumentation.java:603)
at android.app.ContextImpl.startActivity(ContextImpl.java:1101)
at android.content.ContextWrapper.startActivity(ContextWrapper.java:454)
at androidx.test.core.app.InstrumentationActivityInvoker.startActivity(InstrumentationActivityInvoker.java:431)
at androidx.test.core.app.InstrumentationActivityInvoker.startActivity(InstrumentationActivityInvoker.java:437)
at androidx.test.core.app.ActivityScenario.launchInternal(ActivityScenario.java:265)
at androidx.test.core.app.ActivityScenario.launch(ActivityScenario.java:195)
at androidx.test.ext.junit.rules.ActivityScenarioRule.lambda$new$0$ActivityScenarioRule(ActivityScenarioRule.java:70)
at androidx.test.ext.junit.rules.ActivityScenarioRule$$Lambda$0.get(ActivityScenarioRule.java:70)
at androidx.test.ext.junit.rules.ActivityScenarioRule.before(ActivityScenarioRule.java:103)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:50)
at androidx.compose.ui.test.junit4.AndroidComposeTestRule$apply$1$evaluate$1.invoke(AndroidComposeTestRule.android.kt:148)
at androidx.compose.ui.test.junit4.AndroidComposeTestRule$apply$1$evaluate$1.invoke(AndroidComposeTestRule.android.kt:147)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$AndroidComposeUiTestImpl.withDisposableContent(ComposeUiTest.android.kt:476)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1$1$1.invoke(ComposeUiTest.android.kt:294)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withTextInputService(ComposeUiTest.android.kt:360)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withTextInputService(ComposeUiTest.android.kt:217)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1$1.invoke(ComposeUiTest.android.kt:293)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withComposeIdlingResource(ComposeUiTest.android.kt:347)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withComposeIdlingResource(ComposeUiTest.android.kt:217)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1.invoke(ComposeUiTest.android.kt:292)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withWindowRecomposer(ComposeUiTest.android.kt:321)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withWindowRecomposer(ComposeUiTest.android.kt:217)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1.invoke(ComposeUiTest.android.kt:291)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withTestCoroutines(ComposeUiTest.android.kt:334)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withTestCoroutines(ComposeUiTest.android.kt:217)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1.invoke(ComposeUiTest.android.kt:290)
at androidx.compose.ui.test.junit4.EspressoLink.withStrategy(EspressoLink.android.kt:66)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1.invoke(ComposeUiTest.android.kt:289)
at androidx.compose.ui.test.junit4.IdlingResourceRegistry.withRegistry(IdlingResourceRegistry.jvm.kt:157)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1.invoke(ComposeUiTest.android.kt:288)
at androidx.compose.ui.test.junit4.ComposeRootRegistry.withRegistry(ComposeRootRegistry.android.kt:146)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.runTest(ComposeUiTest.android.kt:287)
at androidx.compose.ui.test.junit4.AndroidComposeTestRule$apply$1.evaluate(AndroidComposeTestRule.android.kt:147)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:395)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2361)

The above is from the main branch clone of this repo (reply app). Runs fine but the UI test fails.

I took this main repository as a test for sanity and clean build and run the app on the main branch. Works fine, then run the androidTest and the same above error occurs. I believe it was from my end and have no fixed ideas.

Android studio dolphin: version 2021.3.1 Patch 1

Missing a crucial step of creating the enum class ReplyContentType

The tutorial is missing a crucial step of creating the enum class ReplyContentType. It's necessary to add contentType as a parameter in the ReplyHomeScreen composable. Due to this omission, after following the tutorial, clicking on the email card doesn't display the email details.

Build an adaptive app with adaptive layout: Android Basics with Compose

I was unable to see the preview for the Expanded type:

@Preview(showBackground = true, widthDp = 1000)
@Composable
fun ReplyAppExpandedPreview() {
    ReplyTheme {
        ReplyApp(
            windowSize = WindowWidthSizeClass.Expanded
        )
    }
}

All the other compose previews were successful. You can find the instructions here. This isn't really a problem with the instructions, it is more of an issue with Android Studio.

The Render Issues error output looks like this:
error_output.txt

Build an adaptive app with dynamic navigation: Android Basics with Compose

Build an adaptive app with adaptive layout: Android Basics with Compose

URL of codelab
https://developer.android.com/codelabs/basic-android-kotlin-compose-adaptive-content-for-large-screens?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fandroid-basics-compose-unit-4-pathway-3%3Fhl%3Dko%26authuser%3D1%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fbasic-android-kotlin-compose-adaptive-content-for-large-screens#4

In which task and step of the codelab can this issue be found?

    1. Add automated test for adaptive apps
  • ReplyAppStateRestorationTest.kt
  • fun expandedDevice_selectedEmailEmailRetained_afterConfigChange()

Describe the problem
When running the test,
fun expandedDevice_selectedEmailEmailRetained_afterConfigChange()
failed test
because R.string.details_screen was not found.

Solved by checking that the solution code has the following content.

  • ReplyDetailsScreen.kt
  • fun ReplyDetailsScreen()
  • .testTag(stringResource(id = R.string.details_screen))
    in modifier to argument of LazyColumn

There is no mention of it in the codelab description.

Build an adaptive app with dynamic navigation: Android Basics with Compose

The picture shown after "Implement adaptive UI navigation" step 10 does not match the actual app. At this point, the bottomNavigationBar() Composable has not yet been modified to accept the navigationType parameter, so the bottomNavigaionBar can still be seen, but it is not shown in the accompanying picture.

Build an adaptive app with adaptive layout: Android Basics with Compose

In step 5, section Use annotations to group test for different screen sizes, sub-step 4, the change to test compactDevice_verifyUsingBottomNavigation in ReplyAppTest.kt is repeated instead of the expected change to test expandedDevice_verifyUsingNavigationDrawer:

...
@Test
@TestExpandedWidth
fun expandedDevice_verifyUsingNavigationDrawer() {
...

Crucial! Build an adaptive app with adaptive layout: Android Basics with Compose training course

Issues in the "Improve UI elements for list-detail view" section

Unit 4 -> Adapt for different screen sizes -> Build an adaptive app with an adaptive layout codelab -> Implement adaptive content layout.

In the section titled "Improve UI elements for list-detail view", the objective is to Improve the ReplyDetailsScreen() composable inside the ReplyDetailsScreen.kt file by ridding it from the "extraneous elements" for when it's displayed in the expanded list-detail layout as opposed to the stand-alone layout.

The issue is that when following all the instructions correctly to the end, the app doesn't run properly due to a compilation error. In short, the instructions are incomplete (and illegible).

The intended behavior and the shortcomings

The section attempts to achieve its objective by adding the isFullScreen Boolean parameter to the ReplyDetailsScreen() composable definition to differentiate the composable when used as a standalone or inside the home screen. This addition requires updating all the calls to the composable since its signature definition got altered. The instructions fail to instruct the learners to update all the calls after this alteration to the composable which is the reason for the compilation error.

The ReplyDetailsScreen() is called in multiple files, namely: ReplyHomeScreen.kt and ReplyHomeContent.kt. The call that is causing this issue after following the instructions is the one in the ReplyHomeContent.kt file inside the ReplyListAndDetailContent() composable as the instructions don't address the new parameter there leading to a missing value to an essential parameter.

The solution

To fix the compilation error we can update the call to ReplyDetailsScreen() to include the added parameter isFullScreen. Or by passing a default value for the parameter (which is the approach that the solution code shows at the end of the codelab, yet it's not shown in the instructions).

However, the way that the isFullScreen Boolean parameter is implemented is not optimal and can be confusing, especially for beginners. The name of the parameter conflicts with its usage in the current implementation, and the source for its value is scattered in multiple files. The extraneous element in ReplyDetailsScreen() in the case of a list-details (full screen) layout is the ReplyDetailsScreenTopBar() composable, so one would assume that we can fix this by moving the call to a conditional as follows:

if (!isFullScreen) {
    ReplyDetailsScreenTopBar()
}

Which means that when we are not in full screen, isFullScreen = false, then the top bar is displayed normally, and that's because we want the top bar to show when we are not in list-detail layout since the ReplyDetailsScreen() will be displayed as a stand-alone screen. But when we look at the instructions code:

if (isFullScreen) {
    ReplyDetailsScreenTopBar()
}

It gets confusing because it suggests that the top bar should be displayed when isFullScreen is set to true. However, the list-detail (full screen) layout aims to remove the top bar. It's even more confusing that setting isFullScreen to a default value of false works correctly despite the fact that we would be in a full screen layout when the top bar is not shown in the ReplyDetailsScreen(). This implies that in the current implementation, the isFullScreen is set to false primarily to hide the top bar even when the app is in full screen.

I'm not sure who will see this or if it will be noticed, as I'm also new to GitHub and not very familiar with it. Nevertheless, there's also an issue with the padding for ReplyEmailDetailsCard(). If you are seeing this and are responsible for addressing such issues, you can look into it while you're at it. Thank you!

Build an adaptive app with adaptive layout: Android Basics with Compose

3. Implement adaptive content layout

In last step, Adjust back handling for list-detail view , I think it should be onBackButtonClicked property instead of onBackPressed. When we add onBackPressed project gives compilation error, because we used it as onBackButtonClicked above.
Probably, there is a naming confusion in the documentation.

val activity = LocalContext.current as Activity
        ReplyDetailsScreen(
            replyUiState = replyUiState,
            modifier = Modifier.weight(1f),
            onBackButtonClicked = { activity.finish() }. //or onBackPressed ? 
)

Build an adaptive app with dynamic navigation: Android Basics with Compose

Build an adaptive app with dynamic navigation: Android Basics with Compose

Build an adaptive app with adaptive layout: Android Basics with Compose

In step 5 - Add automated test for adaptive apps

Hi there,
I want to run all tests in solution branch(main) but test named "expandedDevice_selectedEmailEmailRetained_afterConfigChange" gives error below.
Is there anyone run this test successfully ?

java.lang.AssertionError: Failed to perform isDisplayed check.
Reason: Expected exactly '1' node but could not find any node that satisfies: (Text + EditableText contains 'We’re having the best time on the coast! We just witnessed such an amazing sunset and sunrise on the beach. We’re going to be here for another week. Come join us!

Cheers,
Stef and Michael' (ignoreCase: false))


at androidx.compose.ui.test.SemanticsNodeInteraction.fetchOneOrDie(SemanticsNodeInteraction.kt:145)
at androidx.compose.ui.test.SemanticsNodeInteraction.fetchSemanticsNode(SemanticsNodeInteraction.kt:79)
at androidx.compose.ui.test.AndroidAssertions_androidKt.checkIsDisplayed(AndroidAssertions.android.kt:29)
at androidx.compose.ui.test.AssertionsKt.assertIsDisplayed(Assertions.kt:33)
at com.example.reply.test.ReplyAppStateRestorationTest.expandedDevice_selectedEmailEmailRetained_afterConfigChange(ReplyAppStateRestorationTest.kt:69)
 

Build an adaptive app with dynamic navigation: Android Basics with Compose

I have hit a strange behaviour: the Navigation Drawer composable (PermanentNavigationDrawer(...)) displays fine, but no content (defined by content = { ReplyAppContent(...) }) is visible (the inbox as a list of messages). Instead the Navigation Drawer extends all the way to the right edge.

System:

  • Linux KUbuntu 22.04
  • Android Studio Dolphin | 2031.3.1 Patch 1 Built Sept. 30, 2022
  • Project: Reply app cloned from $ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-reply-app.git
  • Emulator: Resizable (Experimental) API 33
  • Physical device: Samsung Nexus 10

The Navigation Drawer behaviour is consistent on both resizable emulator and Nexus device.

I have tried replacing the code inside the content = { ReplyAppContent(...) } with a simple text composable content = { Text(...) }. No text appears.

I am now going to try a Digital Ocean tutorial on Navigation Drawer (https://www.digitalocean.com/community/tutorials/android-navigation-drawer-example-tutorial) and see if the issue persists or gets solved.

Build an adaptive app with dynamic navigation: Android Basics with Compose

Per the instructions in "Build an adaptive app with dynamic navigation", step 2 "App Overview", I downloaded the starter code and attempted a build. The project would not build. See attached message screenshot showing the "This project does not use the Gradle build system. We recommend that you migrate to using the Gradle build system." message.

image

Build an adaptive app with adaptive layout: Android Basics with Compose

Hi. I'm new to this so I'm not sure if I'll be reporting this correctly. So the Expanded View doesn't work.

The first problem was that in the dimens.xml we had this:
<dimen name="profile_image_padding">16.dp</dimen>
and I changed it to:
<dimen name="profile_image_padding">16dp</dimen>

Then I got this error:
java.lang.ClassCastException: class com.android.layoutlib.bridge.android.BridgeContext cannot be cast to class android.app.Activity (com.android.layoutlib.bridge.android.BridgeContext and android.app.Activity are in unnamed module of loader com.intellij.ide.plugins.cl.PluginClassLoader @1bfc6904)
So I added this to the build.gradle file:
implementation "com.google.accompanist:accompanist-insets:0.30.1"
and in the HomeReplyContent.kt I replaced this:

val activity = LocalContext.current as Activity
        ReplyDetailsScreen(
            replyUiState = replyUiState,
            modifier = Modifier.weight(1f),
            onBackPressed = {activity.finish()}
        )

with this:

val activity = LocalContext.current as? ComponentActivity
        ReplyDetailsScreen(
            replyUiState = replyUiState,
            modifier = Modifier.weight(1f),
            onBackPressed = {activity?.finish()}
        )

and imported:
import androidx.activity.ComponentActivity

This managed to fix the problem. I'm new to Android development and so I don't know if this is the most concise way to solve it. Thanks.

Code Issues

I am attempting to get the 'build a test' and provided solution code to work. The code I wrote encounters the error 'Type Mismatch - ComposeUiTest required'.

I've also had issues getting the Compose previews to work. In my code I can get the preview for Compact and Medium to work, but get compose errors on the Expanded windows. The provided solution code does not show any previews at all, they all create errors.

Build an adaptive app with adaptive layout: Android Basics with Compose

the profile_image_padding in dimens.xml has a spurious dot (16.dp) which prevents the expanded preview from rendering. Removing the dot allows the preview to render correctly. I was going to just submit a pull request on github with the change, but I'm not signing a contributer agreement just to remove a dot!

Build an adaptive app with adaptive layout: Android Basics with Compose

I am attempting to get the 'build a test' and provided solution code to work. The code I wrote encounters the error 'Type Mismatch - ComposeUiTest required' on this line:

    val stateRestorationTester = StateRestorationTester(composeTestRule)

I've also had issues getting the Compose previews to work. In my code I can get the preview for Compact and Medium to work, but get compose errors on the Expanded windows. I've narrowed the issue down to the method call to 'ReplyAppContent' in 'ReplyHomeScreen'. If I comment this call out the error does not occur.

The provided solution code (main branch) does not show any previews at all, they all create errors.

Build an adaptive app with adaptive layout: Android Basics with Compose

In Step 5, Add automated test for adaptive apps, around sub-step 6, the string details_screen is used without having been defined in strings.xml. The solution code has this missing line:
<string name="details_screen">Details Screen</string>

Also, the testTag for the details screen isn't being set up; this causes the expandedDevice_selectedEmailEmailRetained_afterConfigChange test to fail. The solution code doesn't contain the missing code, but this works:

ReplyHomeContent.kt in function ReplyListAndDetailContent

val activity = LocalContext.current as Activity
        val detailsScreenContentDescription = stringResource(R.string.details_screen)    // missing
        ReplyDetailsScreen(
            replyUiState = replyUiState,
            modifier = Modifier.weight(1f)
                .testTag(detailsScreenContentDescription),    // missing
            onBackPressed = { activity.finish() }
        )

Build an adaptive app with dynamic navigation: Android Basics with Compose

Task 7 (Implement adaptive navigation layout)
Step (Implement a navigation drawer)
Sub-step 3 should be as following:
In the ReplyApp composable, create a navigationType variable and assign it the appropriate ReplyNavigationType value, according to the screen size in the when statement.

ReplyApp.kt

. . .
import com.example.reply.ui.utils.ReplyNavigationType
. . .
    val navigationType: ReplyNavigationType
    val viewModel: ReplyViewModel = viewModel()
    val replyUiState = viewModel.uiState.collectAsState().value

    when (windowSize) {
        WindowWidthSizeClass.Compact -> {
            navigationType = ReplyNavigationType.BOTTOM_NAVIGATION
        }
        WindowWidthSizeClass.Medium -> {
            navigationType = ReplyNavigationType.NAVIGATION_RAIL
        }
        WindowWidthSizeClass.Expanded -> {
            navigationType = ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER
        }
        else -> {
            navigationType = ReplyNavigationType.BOTTOM_NAVIGATION
        }
    }
. . .

Build an adaptive app with adaptive layout: Android Basics with Compose

Hi,
The description of Codelab https://developer.android.com/codelabs/basic-android-kotlin-compose-adaptive-content-for-large-screens in:

  • step "5 Add automated test for adaptive apps",
    • section "Test for a configuration change in the expanded screen"
      • point 8 "Verify again that the details screen displays the third email after a configuration change."
        says to add a code:
    // Verify that third email is still displayed on the details screen
   composeTestRule.onNodeWithTagForStringId(R.string.details_screen).onChildren()
       .assertAny(hasAnyDescendant(hasText(
           composeTestRule.activity.getString(LocalEmailsDataProvider.allEmails[2].body)))
       )

in ReplyAppStateRestorationTest.kt

The test fails becuse:

java.lang.AssertionError: Failed to assertAny(hasAnyDescendantThat(Text + EditableText contains 'Here are some great shots from my trip...' (ignoreCase: false)))
Reason: Expected exactly '1' node but could not find any node that satisfies: (TestTag = 'Details Screen')

I'm pretty sure, that there was no place in the tutorial to put test tag anywhere in ReplyDetailsScreen.

Build an adaptive app with adaptive layout: Android Basics with Compose

I'm having trouble with loading the ReplyAppExpanded Preview on Android Studios. This error popped up in the Compose Problem screen.
Screenshot_20230217_123629
java.lang.ClassCastException: class com.android.layoutlib.bridge.android.BridgeContext cannot be cast to class android.app.Activity (com.android.layoutlib.bridge.android.BridgeContext and android.app.Activity are in unnamed module of loader com.intellij.ide.plugins.cl.PluginClassLoader @1f1df8b7)
at com.example.reply.ui.ReplyHomeContentKt.ReplyListAndDetailContent(ReplyHomeContent.kt:101)
at com.example.reply.ui.ReplyHomeScreenKt.ReplyAppContent(ReplyHomeScreen.kt:177)
at com.example.reply.ui.ReplyHomeScreenKt.access$ReplyAppContent(ReplyHomeScreen.kt:1)
at com.example.reply.ui.ReplyHomeScreenKt$ReplyHomeScreen$2.invoke(ReplyHomeScreen.kt:114)
at com.example.reply.ui.ReplyHomeScreenKt$ReplyHomeScreen$2.invoke(ReplyHomeScreen.kt:113)
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.material3.NavigationDrawerKt.PermanentNavigationDrawer(NavigationDrawer.kt:436)
at com.example.reply.ui.ReplyHomeScreenKt.ReplyHomeScreen(ReplyHomeScreen.kt:102)
at com.example.reply.ui.ReplyAppKt.ReplyApp-G2aJUZY(ReplyApp.kt:63)
at com.example.reply.ComposableSingletons$MainActivityKt$lambda-3$1.invoke(MainActivity.kt:73)
at com.example.reply.ComposableSingletons$MainActivityKt$lambda-3$1.invoke(MainActivity.kt:72)
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.material3.TextKt.ProvideTextStyle(Text.kt:261)
at androidx.compose.material3.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:81)
at androidx.compose.material3.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.material3.MaterialThemeKt.MaterialTheme(MaterialTheme.kt:73)
at com.example.reply.ui.theme.ThemeKt.ReplyTheme(Theme.kt:115)
at com.example.reply.MainActivityKt.ReplyAppExpandedPreview(MainActivity.kt:72)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at androidx.compose.ui.tooling.ComposableInvoker.invokeComposableMethod(ComposableInvoker.kt:155)
at androidx.compose.ui.tooling.ComposableInvoker.invokeComposable(ComposableInvoker.kt:195)
at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1$composable$1.invoke(ComposeViewAdapter.kt:711)
at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1$composable$1.invoke(ComposeViewAdapter.kt:709)
at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1.invoke(ComposeViewAdapter.kt:746)
at androidx.compose.ui.tooling.ComposeViewAdapter$init$3$1.invoke(ComposeViewAdapter.kt:704)
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.tooling.InspectableKt.Inspectable(Inspectable.kt:61)
at androidx.compose.ui.tooling.ComposeViewAdapter$WrapPreview$1.invoke(ComposeViewAdapter.kt:651)
at androidx.compose.ui.tooling.ComposeViewAdapter$WrapPreview$1.invoke(ComposeViewAdapter.kt:650)
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.tooling.ComposeViewAdapter.WrapPreview(ComposeViewAdapter.kt:645)
at androidx.compose.ui.tooling.ComposeViewAdapter.access$WrapPreview(ComposeViewAdapter.kt:135)
at androidx.compose.ui.tooling.ComposeViewAdapter$init$3.invoke(ComposeViewAdapter.kt:704)
at androidx.compose.ui.tooling.ComposeViewAdapter$init$3.invoke(ComposeViewAdapter.kt:701)
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:404)
at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:250)
at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:249)
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:78)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3248)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3238)
at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341)
at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(Unknown Source)
at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3238)
at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3173)
at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:587)
at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:950)
at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:519)
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:1060)
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:360)
at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:202)
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:1147)
at android.view.View.dispatchAttachedToWindow(View.java:21291)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3491)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3498)
at android.view.AttachInfo_Accessor.setAttachInfo(AttachInfo_Accessor.java:58)
at com.android.layoutlib.bridge.impl.RenderSessionImpl.inflate(RenderSessionImpl.java:367)
at com.android.layoutlib.bridge.Bridge.createSession(Bridge.java:443)
at com.android.tools.idea.layoutlib.LayoutLibrary.createSession(LayoutLibrary.java:121)
at com.android.tools.idea.rendering.RenderTask.createRenderSession(RenderTask.java:722)
at com.android.tools.idea.rendering.RenderTask.lambda$inflate$9(RenderTask.java:879)
at com.android.tools.idea.rendering.RenderExecutor$runAsyncActionWithTimeout$3.run(RenderExecutor.kt:194)
at com.android.tools.idea.rendering.RenderExecutor$PriorityRunnable.run(RenderExecutor.kt:292)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)
What should I do?

Build an adaptive app with dynamic navigation: Android Basics with Compose

Build an adaptive app with adaptive layout: Android Basics with Compose

https://developer.android.com/codelabs/basic-android-kotlin-compose-adaptive-content-for-large-screens?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fandroid-basics-compose-unit-4-pathway-3%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fbasic-android-kotlin-compose-adaptive-content-for-large-screens#2

...
if (navigationType == ReplyNavigationType.PERMANENT_NAVIGATION_DRAWER) {
PermanentNavigationDrawer(
drawerContent = {
NavigationDrawerContent(
selectedDestination = replyUiState.currentMailbox,
onTabPressed = onTabPressed,
navigationItemContentList = navigationItemContentList
)
}
) {
... This is wrong, does not work, PermamentDrawerSheet is missing.

Build an adaptive app with dynamic navigation: Android Basics with Compose

In section 7 Implement adaptive navigation layout step 11 Run the app in Tablet mode, it should show both navigation drawer and bottom navigation.
image

To fix it, move up step 6 in the bottom of implementation rail navigation (Set the visible parameter when the ReplyNavigationType value is BOTTOM_NAVIGATION.
) after step 11.

Build an adaptive app with dynamic navigation: Android Basics with Compose

Wrong `onEmailCardPressed` handler for the case of `LIST_AND_CONTENT`

onEmailCardPressed is the same as in the previous path and when in tablet mode we click on an email below the first one
the isHomePage model property is reset to false and we go from list and content to just details view

Below is part of how I modified the code. The other part is obvious I guess.

onEmailCardPressed = { email: Email ->
            viewModel.updateDetailsScreenStates(
                email = email,
                isHomepage = contentType == ReplyContentType.LIST_AND_DETAIL
            )
        },

Build an adaptive app with adaptive layout: Android Basics with Compose: running the expanded device test on the Reply App State Restoration Test

When I ran the expandedDevice_selectedEmailEmailRetained_afterConfigChange() function on the Reply App State Restoration Test class file, this error popped up:
java.lang.AssertionError: Failed to assertAny(hasAnyDescendantThat(Text + EditableText contains 'We’re having the best time on the coast! We just witnessed such an amazing sunset and sunrise on the beach. We’re going to be here for another week. Come join us!

Cheers,
Stef and Michael' (ignoreCase: false)))
Reason: Expected exactly '1' node but could not find any node that satisfies: (ContentDescription = 'Details Screen' (ignoreCase: false))

at androidx.compose.ui.test.SemanticsSelector.map(SemanticsSelector.kt:45)
at androidx.compose.ui.test.SemanticsNodeInteractionCollection.fetchSemanticsNodes(SemanticsNodeInteraction.kt:222)
at androidx.compose.ui.test.SemanticsNodeInteractionCollection.fetchSemanticsNodes$default(SemanticsNodeInteraction.kt:216)
at androidx.compose.ui.test.AssertionsKt.assertAny(Assertions.kt:314)
at com.example.reply.test.ReplyAppStateRestorationTest.expandedDevice_selectedEmailEmailRetained_afterConfigChange(ReplyAppStateRestorationTest.kt:60)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:54)
at androidx.compose.ui.test.junit4.AndroidComposeTestRule$apply$1$evaluate$1.invoke(AndroidComposeTestRule.android.kt:148)
at androidx.compose.ui.test.junit4.AndroidComposeTestRule$apply$1$evaluate$1.invoke(AndroidComposeTestRule.android.kt:147)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$AndroidComposeUiTestImpl.withDisposableContent(ComposeUiTest.android.kt:476)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1$1$1.invoke(ComposeUiTest.android.kt:294)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withTextInputService(ComposeUiTest.android.kt:360)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withTextInputService(ComposeUiTest.android.kt:217)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1$1.invoke(ComposeUiTest.android.kt:293)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withComposeIdlingResource(ComposeUiTest.android.kt:347)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withComposeIdlingResource(ComposeUiTest.android.kt:217)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1.invoke(ComposeUiTest.android.kt:292)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withWindowRecomposer(ComposeUiTest.android.kt:321)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withWindowRecomposer(ComposeUiTest.android.kt:217)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1.invoke(ComposeUiTest.android.kt:291)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.withTestCoroutines(ComposeUiTest.android.kt:334)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$withTestCoroutines(ComposeUiTest.android.kt:217)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1.invoke(ComposeUiTest.android.kt:290)
at androidx.compose.ui.test.junit4.EspressoLink.withStrategy(EspressoLink.android.kt:66)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1.invoke(ComposeUiTest.android.kt:289)
at androidx.compose.ui.test.junit4.IdlingResourceRegistry.withRegistry(IdlingResourceRegistry.jvm.kt:157)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1.invoke(ComposeUiTest.android.kt:288)
at androidx.compose.ui.test.junit4.ComposeRootRegistry.withRegistry(ComposeRootRegistry.android.kt:146)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.runTest(ComposeUiTest.android.kt:287)
at androidx.compose.ui.test.junit4.AndroidComposeTestRule$apply$1.evaluate(AndroidComposeTestRule.android.kt:147)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:395)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2361)

Here are some screenshots as well:
image
image
What should I do?

Build an adaptive app with dynamic navigation: Android Basics with Compose

In this project the enum class ReplyNavigationType {
BOTTOM_NAVIGATION, NAVIGATION_RAIL, PERMANENT_NAVIGATION_DRAWER
}
is declared twice if the instructions are followed to the letter.
This happens within its own class com/example/reply/ui/utils/ReplyNavigationType.kt and within com/example/reply/ui/utils/WindowStateUtils.kt.

To fix, simply delete one or other of them.

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.