diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 48a51d7..c66b117 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -94,6 +94,7 @@ dependencies { implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) + implementation(libs.androidx.compose.material.icons.extended) implementation(libs.coil.compose) implementation(libs.koin.core) implementation(libs.koin.android) diff --git a/app/src/main/java/app/plugbrain/android/ui/designsystem/components/listitem/LeadingIcon.kt b/app/src/main/java/app/plugbrain/android/ui/designsystem/components/listitem/LeadingIcon.kt new file mode 100644 index 0000000..3f12fc2 --- /dev/null +++ b/app/src/main/java/app/plugbrain/android/ui/designsystem/components/listitem/LeadingIcon.kt @@ -0,0 +1,36 @@ +package app.plugbrain.android.ui.designsystem.components.listitem + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp + +@Composable +internal fun LeadingIcon( + modifier: Modifier = Modifier.Companion, + icon: ImageVector, + isSelected: Boolean, +) { + val tint = + if (isSelected) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurfaceVariant + val background = + if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surfaceVariant + Icon( + imageVector = icon, + contentDescription = "", + tint = tint, + modifier = modifier + .background( + color = background, + shape = RoundedCornerShape(4.dp), + ) + .size(40.dp) + .padding(8.dp), + ) +} diff --git a/app/src/main/java/app/plugbrain/android/ui/designsystem/components/listitem/PlugInfoListItem.kt b/app/src/main/java/app/plugbrain/android/ui/designsystem/components/listitem/PlugInfoListItem.kt new file mode 100644 index 0000000..b291d02 --- /dev/null +++ b/app/src/main/java/app/plugbrain/android/ui/designsystem/components/listitem/PlugInfoListItem.kt @@ -0,0 +1,86 @@ +package app.plugbrain.android.ui.designsystem.components.listitem + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.TrendingUp +import androidx.compose.material.icons.rounded.LockOpen +import androidx.compose.material.icons.rounded.TrackChanges +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import app.plugbrain.android.ui.theme.MathlockAppTheme + +@Composable +fun PlugInfoListItem( + title: String, + description: String, + icon: ImageVector, +) { + Row( + horizontalArrangement = Arrangement.spacedBy(24.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .background(MaterialTheme.colorScheme.surface), + ) { + LeadingIcon( + icon = icon, + isSelected = false, + modifier = Modifier.align(Alignment.Top).padding(top = 3.dp), + ) + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier.weight(1f), + ) { + Text(title, style = MaterialTheme.typography.titleLarge, color = MaterialTheme.colorScheme.onSurface) + Text(description, style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurface) + } + } +} + +@Preview( + name = "Light Mode", + showBackground = true, + uiMode = Configuration.UI_MODE_NIGHT_NO, +) +@Preview( + name = "Dark Mode", + showBackground = true, + uiMode = Configuration.UI_MODE_NIGHT_YES, +) +@Composable +private fun PermissionListItemPreview() { + MathlockAppTheme(dynamicColor = false) { + Column( + verticalArrangement = Arrangement.spacedBy(24.dp), + modifier = Modifier + .background(MaterialTheme.colorScheme.surface) + .padding(32.dp), + ) { + PlugInfoListItem( + title = "Focus your attention", + description = "Spend less time on apps that distract you.", + icon = Icons.Rounded.TrackChanges, + ) + PlugInfoListItem( + title = "Unlock with a challenge", + description = "Solve a quick math challenge to keep using the app.", + icon = Icons.Rounded.LockOpen, + ) + PlugInfoListItem( + title = "Gets harder the longer you stay", + description = "Challenges increase in difficulty the longer you keep scrolling.", + icon = Icons.AutoMirrored.Rounded.TrendingUp, + ) + } + } +} diff --git a/app/src/main/java/app/plugbrain/android/ui/designsystem/components/listitem/PlugPermissionListItem.kt b/app/src/main/java/app/plugbrain/android/ui/designsystem/components/listitem/PlugPermissionListItem.kt new file mode 100644 index 0000000..dbd0957 --- /dev/null +++ b/app/src/main/java/app/plugbrain/android/ui/designsystem/components/listitem/PlugPermissionListItem.kt @@ -0,0 +1,103 @@ +package app.plugbrain.android.ui.designsystem.components.listitem + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Search +import androidx.compose.material.icons.rounded.Layers +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import app.plugbrain.android.ui.theme.MathlockAppTheme + +@Composable +fun PlugPermissionListItem( + title: String, + description: String, + icon: ImageVector, + isGranted: Boolean, + onClick: () -> Unit, +) { + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .alpha(if (isGranted) 0.5f else 1f) + .border( + width = 1.dp, + color = MaterialTheme.colorScheme.outlineVariant, + shape = RoundedCornerShape(8.dp), + ) + .background(MaterialTheme.colorScheme.surface) + .background( + color = if (isGranted) MaterialTheme.colorScheme.primary.copy(0.1f) else MaterialTheme.colorScheme.surface, + shape = RoundedCornerShape(8.dp), + ) + .clickable(onClick = onClick) + .padding(vertical = 12.dp, horizontal = 16.dp), + ) { + LeadingIcon( + icon = icon, + isSelected = isGranted, + modifier = Modifier.align(Alignment.Top).padding(top = 3.dp), + ) + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier.weight(1f), + ) { + Text(title, style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onSurface) + Text(description, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurface) + } + TrailingIcon(isGranted) + } +} + +@Preview( + name = "Light Mode", + showBackground = true, + uiMode = Configuration.UI_MODE_NIGHT_NO, +) +@Preview( + name = "Dark Mode", + showBackground = true, + uiMode = Configuration.UI_MODE_NIGHT_YES, +) +@Composable +private fun PermissionListItemPreview() { + MathlockAppTheme(dynamicColor = false) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier + .background(MaterialTheme.colorScheme.surface) + .padding(32.dp), + ) { + PlugPermissionListItem( + title = "App Usage access", + description = "Lets PlugBrain track app usage so it can block distracting apps at the right time.", + isGranted = true, + icon = Icons.Filled.Search, + onClick = {}, + ) + PlugPermissionListItem( + title = "Display over other apps", + description = "Allows PlugBrain to display a challenge while you use a distracting app.", + isGranted = false, + icon = Icons.Rounded.Layers, + onClick = {}, + ) + } + } +} diff --git a/app/src/main/java/app/plugbrain/android/ui/designsystem/components/listitem/TrailingIcon.kt b/app/src/main/java/app/plugbrain/android/ui/designsystem/components/listitem/TrailingIcon.kt new file mode 100644 index 0000000..5c7fe1d --- /dev/null +++ b/app/src/main/java/app/plugbrain/android/ui/designsystem/components/listitem/TrailingIcon.kt @@ -0,0 +1,32 @@ +package app.plugbrain.android.ui.designsystem.components.listitem + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.KeyboardArrowRight +import androidx.compose.material.icons.rounded.Check +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp + +@Composable +internal fun TrailingIcon(isSelected: Boolean) { + val tint = + if (isSelected) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurfaceVariant + val background = + if (isSelected) MaterialTheme.colorScheme.primary else Color.Companion.Transparent + Icon( + imageVector = if (isSelected) Icons.Rounded.Check else Icons.AutoMirrored.Rounded.KeyboardArrowRight, + contentDescription = "", + tint = tint, + modifier = Modifier.Companion + .background(color = background, shape = RoundedCornerShape(50)) + .size(24.dp) + .then(if (isSelected) Modifier.Companion.padding(4.dp) else Modifier.Companion), + ) +} diff --git a/app/src/main/java/app/plugbrain/android/ui/theme/Theme.kt b/app/src/main/java/app/plugbrain/android/ui/theme/Theme.kt index 31e18f3..b681f04 100644 --- a/app/src/main/java/app/plugbrain/android/ui/theme/Theme.kt +++ b/app/src/main/java/app/plugbrain/android/ui/theme/Theme.kt @@ -21,9 +21,11 @@ private val DarkColorScheme = background = Neutral900, error = Error600, outline = Neutral200, + outlineVariant = Neutral500, surfaceVariant = Neutral500, onSurfaceVariant = Neutral300, tertiary = Success300, + onTertiary = White, ) private val LightColorScheme = @@ -37,9 +39,11 @@ private val LightColorScheme = background = White, error = Error500, outline = Neutral300, + outlineVariant = Neutral200, surfaceVariant = Neutral100, - onSurfaceVariant = Neutral300, + onSurfaceVariant = Neutral400, tertiary = Success500, + onTertiary = White, ) @Composable diff --git a/app/src/main/java/app/plugbrain/android/ui/theme/Type.kt b/app/src/main/java/app/plugbrain/android/ui/theme/Type.kt index b8928c4..55d9d79 100644 --- a/app/src/main/java/app/plugbrain/android/ui/theme/Type.kt +++ b/app/src/main/java/app/plugbrain/android/ui/theme/Type.kt @@ -40,6 +40,13 @@ val Typography = ), titleLarge = TextStyle( + fontFamily = SoraFontFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 16.sp, + lineHeight = 22.sp, + ), + + titleMedium = TextStyle( fontFamily = SoraFontFamily, fontWeight = FontWeight.SemiBold, fontSize = 14.sp, @@ -53,6 +60,13 @@ val Typography = lineHeight = 22.sp, ), + bodyMedium = TextStyle( + fontFamily = SoraFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + lineHeight = 17.sp, + ), + labelLarge = TextStyle( fontFamily = SoraFontFamily, fontWeight = FontWeight.SemiBold, diff --git a/app/src/test/java/app/plugbrain/android/screenshots/InfoListItemScreenshotTest.kt b/app/src/test/java/app/plugbrain/android/screenshots/InfoListItemScreenshotTest.kt new file mode 100644 index 0000000..9425e70 --- /dev/null +++ b/app/src/test/java/app/plugbrain/android/screenshots/InfoListItemScreenshotTest.kt @@ -0,0 +1,71 @@ +package app.plugbrain.android.screenshots + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.TrendingUp +import androidx.compose.material.icons.rounded.LockOpen +import androidx.compose.material.icons.rounded.TrackChanges +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import app.cash.paparazzi.DeviceConfig +import app.cash.paparazzi.Paparazzi +import app.plugbrain.android.ui.designsystem.components.listitem.PlugInfoListItem +import app.plugbrain.android.ui.theme.MathlockAppTheme +import org.junit.Rule +import org.junit.Test + +class InfoListItemScreenshotTest { + @get:Rule + val paparazzi = + Paparazzi( + deviceConfig = DeviceConfig.PIXEL_5, + ) + + @Test + fun permissionListItemGrantedTest() { + paparazzi.snapshot { + infoItemTest() + } + } + + @Test + fun permissionListItemDarkModeTest() { + paparazzi.snapshot { + infoItemTest(darkTheme = true) + } + } + + @Composable + private fun infoItemTest(darkTheme: Boolean = false) { + MathlockAppTheme(dynamicColor = false, darkTheme = darkTheme) { + Column( + verticalArrangement = Arrangement.spacedBy(24.dp), + modifier = + Modifier + .background(MaterialTheme.colorScheme.surface) + .padding(32.dp), + ) { + PlugInfoListItem( + title = "Focus your attention", + description = "Spend less time on apps that distract you.", + icon = Icons.Rounded.TrackChanges, + ) + PlugInfoListItem( + title = "Unlock with a challenge", + description = "Solve a quick math challenge to keep using the app.", + icon = Icons.Rounded.LockOpen, + ) + PlugInfoListItem( + title = "Gets harder the longer you stay", + description = "Challenges increase in difficulty the longer you keep scrolling.", + icon = Icons.AutoMirrored.Rounded.TrendingUp, + ) + } + } + } +} diff --git a/app/src/test/java/app/plugbrain/android/screenshots/PermissionListItemScreenshotTest.kt b/app/src/test/java/app/plugbrain/android/screenshots/PermissionListItemScreenshotTest.kt new file mode 100644 index 0000000..6516aff --- /dev/null +++ b/app/src/test/java/app/plugbrain/android/screenshots/PermissionListItemScreenshotTest.kt @@ -0,0 +1,69 @@ +package app.plugbrain.android.screenshots + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Search +import androidx.compose.material.icons.rounded.Layers +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import app.cash.paparazzi.DeviceConfig +import app.cash.paparazzi.Paparazzi +import app.plugbrain.android.ui.designsystem.components.listitem.PlugPermissionListItem +import app.plugbrain.android.ui.theme.MathlockAppTheme +import org.junit.Rule +import org.junit.Test + +class PermissionListItemScreenshotTest { + @get:Rule + val paparazzi = + Paparazzi( + deviceConfig = DeviceConfig.PIXEL_5, + ) + + @Test + fun permissionListItemGrantedTest() { + paparazzi.snapshot { + permissionItemTest() + } + } + + @Test + fun permissionListItemDarkModeTest() { + paparazzi.snapshot { + permissionItemTest(darkTheme = true) + } + } + + @Composable + private fun permissionItemTest(darkTheme: Boolean = false) { + MathlockAppTheme(dynamicColor = false, darkTheme = darkTheme) { + Column( + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = + Modifier + .background(MaterialTheme.colorScheme.surface) + .padding(32.dp), + ) { + PlugPermissionListItem( + title = "App Usage access", + description = "Lets PlugBrain track app usage so it can block distracting apps at the right time.", + isGranted = true, + icon = Icons.Filled.Search, + onClick = {}, + ) + PlugPermissionListItem( + title = "Display over other apps", + description = "Allows PlugBrain to display a challenge while you use a distracting app.", + isGranted = false, + icon = Icons.Rounded.Layers, + onClick = {}, + ) + } + } + } +} diff --git a/app/src/test/snapshots/images/app.plugbrain.android.screenshots_InfoListItemScreenshotTest_permissionListItemDarkModeTest.png b/app/src/test/snapshots/images/app.plugbrain.android.screenshots_InfoListItemScreenshotTest_permissionListItemDarkModeTest.png new file mode 100644 index 0000000..900475b Binary files /dev/null and b/app/src/test/snapshots/images/app.plugbrain.android.screenshots_InfoListItemScreenshotTest_permissionListItemDarkModeTest.png differ diff --git a/app/src/test/snapshots/images/app.plugbrain.android.screenshots_InfoListItemScreenshotTest_permissionListItemGrantedTest.png b/app/src/test/snapshots/images/app.plugbrain.android.screenshots_InfoListItemScreenshotTest_permissionListItemGrantedTest.png new file mode 100644 index 0000000..f9c7947 Binary files /dev/null and b/app/src/test/snapshots/images/app.plugbrain.android.screenshots_InfoListItemScreenshotTest_permissionListItemGrantedTest.png differ diff --git a/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PermissionListItemScreenshotTest_permissionListItemDarkModeTest.png b/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PermissionListItemScreenshotTest_permissionListItemDarkModeTest.png new file mode 100644 index 0000000..d10553e Binary files /dev/null and b/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PermissionListItemScreenshotTest_permissionListItemDarkModeTest.png differ diff --git a/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PermissionListItemScreenshotTest_permissionListItemGrantedTest.png b/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PermissionListItemScreenshotTest_permissionListItemGrantedTest.png new file mode 100644 index 0000000..ee3e5a7 Binary files /dev/null and b/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PermissionListItemScreenshotTest_permissionListItemGrantedTest.png differ diff --git a/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PlugBottomSheetScreenshotTest_plugBottomSheetInputTests.png b/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PlugBottomSheetScreenshotTest_plugBottomSheetInputTests.png index 07a5230..8671b7d 100644 Binary files a/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PlugBottomSheetScreenshotTest_plugBottomSheetInputTests.png and b/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PlugBottomSheetScreenshotTest_plugBottomSheetInputTests.png differ diff --git a/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PlugNumericalInputScreenshotTest_plugNumericalInputTests.png b/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PlugNumericalInputScreenshotTest_plugNumericalInputTests.png index 3a3c816..301f231 100644 Binary files a/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PlugNumericalInputScreenshotTest_plugNumericalInputTests.png and b/app/src/test/snapshots/images/app.plugbrain.android.screenshots_PlugNumericalInputScreenshotTest_plugNumericalInputTests.png differ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 763bfcb..cc51d4a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -33,6 +33,7 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } +androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" } #Coil Image loader coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilCompose" }