Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
5 changes: 2 additions & 3 deletions animations/canvas/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import com.guru.composecookbook.build.dependencies.addComposeOfficialDependencies

plugins {
/** See [common-compose-module-configs-script-plugin.gradle.kts] file */
id("common-compose-module-configs-script-plugin")
Expand All @@ -9,5 +7,6 @@ android { namespace = "com.guru.composecookbook.canvas" }

dependencies {
implementation(project(":theme"))
addComposeOfficialDependencies()
implementation(platform(libs.androidx.compose.bom))
implementation(libs.bundles.compose.official)
}
11 changes: 4 additions & 7 deletions animations/lottie/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import com.guru.composecookbook.build.dependencies.addComposeOfficialDependencies
import com.guru.composecookbook.build.dependencies.addCoreAndroidDependencies
import com.guru.composecookbook.build.dependencies.addThirdPartyUiDependencies

plugins {
/** See [common-compose-module-configs-script-plugin.gradle.kts] file */
id("common-compose-module-configs-script-plugin")
Expand All @@ -10,7 +6,8 @@ plugins {
android { namespace = "com.guru.composecookbook.lottie" }

dependencies {
addComposeOfficialDependencies()
addCoreAndroidDependencies()
addThirdPartyUiDependencies()
implementation(platform(libs.androidx.compose.bom))
implementation(libs.bundles.compose.official)
implementation(libs.bundles.core.android)
implementation(libs.bundles.thirdparty.ui)
}
50 changes: 20 additions & 30 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,19 +1,4 @@
import com.guru.composecookbook.build.configurations.ProjectConfigs
import com.guru.composecookbook.build.dependencies.addAndroidInstrumentationTestsDependencies
import com.guru.composecookbook.build.dependencies.addBiometricDependency
import com.guru.composecookbook.build.dependencies.addComposeDebugDependencies
import com.guru.composecookbook.build.dependencies.addComposeOfficialDependencies
import com.guru.composecookbook.build.dependencies.addComposeThirdPartyDependencies
import com.guru.composecookbook.build.dependencies.addCoreAndroidDependencies
import com.guru.composecookbook.build.dependencies.addCoreAndroidUiDependencies
import com.guru.composecookbook.build.dependencies.addDataDependencies
import com.guru.composecookbook.build.dependencies.addGoogleAndroidDependencies
import com.guru.composecookbook.build.dependencies.addJunit5TestDependencies
import com.guru.composecookbook.build.dependencies.addKotlinDependencies
import com.guru.composecookbook.build.dependencies.addKotlinTestDependencies
import com.guru.composecookbook.build.dependencies.addNetworkingDependencies
import com.guru.composecookbook.build.dependencies.addThirdPartyUiDependencies
import com.guru.composecookbook.build.dependencies.addThirdPartyUnitTestsDependencies

plugins {
id("com.android.application")
Expand Down Expand Up @@ -86,25 +71,30 @@ dependencies {
implementation(project(":animations:canvas"))
implementation(project(":animations:lottie"))

addKotlinDependencies()
implementation(libs.bundles.kotlin)

addDataDependencies()
implementation(platform(libs.androidx.compose.bom))
ksp(libs.androidx.room.compiler)
implementation(libs.bundles.data)

addComposeOfficialDependencies()
addComposeDebugDependencies()
addComposeThirdPartyDependencies()
implementation(platform(libs.androidx.compose.bom))
implementation(libs.bundles.compose.official)
debugImplementation(platform(libs.androidx.compose.bom))
debugImplementation(libs.bundles.compose.debug)
implementation(libs.bundles.compose.thirdparty)

addThirdPartyUiDependencies()
implementation(libs.bundles.thirdparty.ui)

addCoreAndroidDependencies()
addCoreAndroidUiDependencies()
addGoogleAndroidDependencies()
addNetworkingDependencies()
implementation(libs.bundles.core.android)
implementation(libs.bundles.core.android.ui)
implementation(libs.bundles.google.android)
implementation(libs.bundles.networking)

addKotlinTestDependencies()
addJunit5TestDependencies()
addThirdPartyUnitTestsDependencies()
testImplementation(libs.bundles.kotlin.test)
testImplementation(libs.bundles.junit5.test)
testImplementation(libs.truth)

addAndroidInstrumentationTestsDependencies()
addBiometricDependency()
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.bundles.instrumentation.test)
implementation(libs.androidx.biometric)
}
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