Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
<a href="https://github.com/Gurupreet/ComposeCookBook/releases/download/latest-master/app-debug.apk" >
<img src = "https://img.shields.io/badge/Master-Master?color=7885FF&label=Sample%20App&logo=android&style=for-the-badge" />
</a>
<a href = "https://developer.android.com/jetpack/androidx/versions/all-channel#december_16_2020">
<img src = "https://img.shields.io/badge/Jetpack%20Compose-1.0.1-brightgreen" />
<a href = "https://developer.android.com/develop/ui/compose/bom/bom-mapping">
<img src = "https://img.shields.io/badge/Jetpack%20Compose-BOM%202024.12.01-brightgreen" />
</a>
<a href = "https://github.com/Gurupreet/ComposeCookBook/actions/workflows/android.yml">
<img src = "https://github.com/Gurupreet/ComposeCookBook/actions/workflows/android.yml/badge.svg" />
Expand Down Expand Up @@ -100,9 +100,12 @@ Please get **Android Studio Bumblebee latest Canary** [from here](https://develo
- __Others:__ After the above steps feel free to deep dive into Tablayouts, carousel, Dialogs and BottomSheets


### Adaptive layouts (large screens)
- `NavigationSuiteScaffold` switches between bottom bar, rail and drawer based on window size
- `ListDetailPaneScaffold` two-pane layout on tablets/foldables — find it under "Adaptive UI" on the home screen

## Coming Soon
- Some of the features that will be available in coming weeks
- Advance lists: Pull Refresh, Swipe lists etc
- Clean Architecture Sample with coroutines.
- Advance canvas drawing.
Much more in pipeline stay tuned!!
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.guru.composecookbook.ui.home.adaptive

import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import org.junit.Rule
import org.junit.Test

class AdaptiveUIActivityTest {

@get:Rule val composeTestRule = createComposeRule()

@Test
fun everyNavigationDestinationIsLabelled() {
composeTestRule.setContent { AdaptiveDemoScreen() }

composeTestRule.onNodeWithText("Albums").assertIsDisplayed()
composeTestRule.onNodeWithText("Favorites").assertIsDisplayed()
composeTestRule.onNodeWithText("Profile").assertIsDisplayed()
}

@Test
fun albumsDestinationIsShownOnLaunch() {
composeTestRule.setContent { AdaptiveDemoScreen() }

composeTestRule.onNodeWithText("Perfect").assertIsDisplayed()
// Two albums in the sample data are by Ed Sheeran, so the artist alone is not unique.
composeTestRule.onAllNodesWithText("Ed Sheeran").onFirst().assertIsDisplayed()
}

@Test
fun selectingFavoritesReportsTheCurrentWindowSizeClasses() {
composeTestRule.setContent { AdaptiveDemoScreen() }

composeTestRule.onNodeWithText("Favorites").performClick()
composeTestRule.waitForIdle()

composeTestRule.onNodeWithText("Width size class:", substring = true).assertIsDisplayed()
composeTestRule.onNodeWithText("Height size class:", substring = true).assertIsDisplayed()
}

@Test
fun albumListShowsSongArtistAndGenre() {
composeTestRule.setContent { AlbumListDetail() }

composeTestRule.onNodeWithText("Perfect").assertIsDisplayed()
// Artist and genre both repeat across the sample data; the song title is the unique field.
composeTestRule.onAllNodesWithText("Ed Sheeran").onFirst().assertIsDisplayed()
composeTestRule.onAllNodesWithText("Pop").onFirst().assertIsDisplayed()
}

@Test
fun tappingAnAlbumOpensItsDetailPane() {
composeTestRule.setContent { AlbumListDetail() }

composeTestRule.onNodeWithText("Perfect").performClick()
composeTestRule.waitForIdle()

composeTestRule.onNodeWithText("Album by Ed Sheeran-2016").assertIsDisplayed()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.guru.composecookbook.ui.home.advancelists

import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithContentDescription
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onParent
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeLeft
import androidx.compose.ui.test.swipeRight
import org.junit.Rule
import org.junit.Test

class SwipeableListsTest {

@get:Rule val composeTestRule = createComposeRule()

@Test
fun albumsAreListedOnFirstComposition() {
composeTestRule.setContent { SwipeableLists() }

composeTestRule.onNodeWithText("Perfect").assertIsDisplayed()
composeTestRule.onNodeWithText("Havana").assertIsDisplayed()
// Two albums in the sample data are by Ed Sheeran, so the artist alone is not unique.
composeTestRule.onAllNodesWithText("Ed Sheeran").onFirst().assertIsDisplayed()
}

@Test
fun everyRowExposesItsOverflowButtonToAccessibilityServices() {
composeTestRule.setContent { SwipeableLists() }

composeTestRule.onAllNodesWithContentDescription("More options").onFirst().assertIsDisplayed()
}

@Test
fun swipingEndToStartDeletesTheRowAndOffersUndo() {
composeTestRule.setContent { SwipeableLists() }

// The swipe must be dispatched to the row, not to the song title: the title node is only a
// few pixels wide, well short of the box's 50% positional threshold.
composeTestRule.onNodeWithText("Perfect").onParent().performTouchInput { swipeLeft() }
composeTestRule.waitForIdle()

composeTestRule.onNodeWithText("Perfect").assertDoesNotExist()
composeTestRule.onNodeWithText("Perfect deleted").assertIsDisplayed()
composeTestRule.onNodeWithText("Undo").assertIsDisplayed()
}

@Test
fun swipingStartToEndArchivesTheRowAndOffersUndo() {
composeTestRule.setContent { SwipeableLists() }

composeTestRule.onNodeWithText("Perfect").onParent().performTouchInput { swipeRight() }
composeTestRule.waitForIdle()

composeTestRule.onNodeWithText("Perfect").assertDoesNotExist()
composeTestRule.onNodeWithText("Perfect archived").assertIsDisplayed()
}

@Test
fun undoRestoresASwipedAwayRow() {
composeTestRule.setContent { SwipeableLists() }

composeTestRule.onNodeWithText("Perfect").onParent().performTouchInput { swipeLeft() }
composeTestRule.waitForIdle()
composeTestRule.onNodeWithText("Undo").performClick()
composeTestRule.waitForIdle()

composeTestRule.onNodeWithText("Perfect").assertIsDisplayed()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.guru.composecookbook.ui.home.pullrefreshdemos

import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeDown
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test

class PullRefreshListTest {

@get:Rule val composeTestRule = createComposeRule()

@Test
fun firstAlbumIsDisplayedInitially() {
composeTestRule.setContent { PullRefreshList(onPullRefresh = {}) }

composeTestRule.onNodeWithText("Perfect").assertIsDisplayed()
// Two albums in the sample data are by Ed Sheeran, so this row text is not unique.
composeTestRule
.onAllNodesWithText("Ed Sheeran, Album by Ed Sheeran-2016")
.onFirst()
.assertIsDisplayed()
}

@Test
fun refreshedAlbumIsAbsentBeforeAnyRefresh() {
composeTestRule.setContent { PullRefreshList(onPullRefresh = {}) }

// AlbumsDataProvider.album is only prepended by a refresh, so it must not be there yet.
// Match on the artist/description line: it is the one string unique to that album.
composeTestRule.onNodeWithText("Adele, Album by Adele-2016").assertDoesNotExist()
}

@Test
fun pullingDownInvokesTheRefreshCallback() {
var invocations = 0
composeTestRule.setContent { PullRefreshList(onPullRefresh = { invocations++ }) }

// The gesture has to travel far enough to cross PullToRefreshBox's threshold, so it is
// dispatched to the whole screen rather than to a single row.
composeTestRule.onRoot().performTouchInput { swipeDown() }
composeTestRule.waitForIdle()

assertEquals(1, invocations)
}
}
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
android:name=".ui.templates.TemplatesActivity"
android:label="@string/title_activity_templates"
android:theme="@style/Theme.ComposeCookBook.NoActionBar" />
<activity
android:name=".ui.home.adaptive.AdaptiveUIActivity"
android:label="@string/title_activity_adaptive"
android:theme="@style/Theme.ComposeCookBook.NoActionBar" />
<activity
android:name=".ui.home.advancelists.AdvanceListsActivity"
android:label="@string/title_activity_advance_lists"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SmallTopAppBar
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
Expand Down Expand Up @@ -39,7 +39,7 @@ fun AnimationScreen() {
Scaffold(
modifier = Modifier.testTag(TestTags.ANIM_SCREEN_ROOT),
topBar = {
SmallTopAppBar(
TopAppBar(
title = { Text(text = "Animations") },
navigationIcon = {
IconButton(onClick = { animateIcon = !animateIcon }) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import com.guru.composecookbook.theme.components.Material3Card
import com.guru.composecookbook.theme.green500
import com.guru.composecookbook.theme.orange500
import com.guru.composecookbook.theme.purple
import com.guru.composecookbook.ui.home.adaptive.AdaptiveUIActivity
import com.guru.composecookbook.ui.home.advancelists.AdvanceListsActivity
import com.guru.composecookbook.ui.home.customfling.FlingListActivity
import com.guru.composecookbook.ui.home.dialogs.DialogsActivity
Expand Down Expand Up @@ -301,6 +302,9 @@ fun homeItemClicked(homeScreenItems: HomeScreenItems, context: Context, isDarkTh
HomeScreenItems.MotionLayout -> {
DynamicUIActivity.newIntent(context, DynamicUiType.MOTIONLAYOUT.name, isDarkTheme)
}
HomeScreenItems.AdaptiveUI -> {
AdaptiveUIActivity.newIntent(context, isDarkTheme)
}
}
context.startActivity(intent)
}
Expand Down
Loading
Loading