diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml
new file mode 100644
index 0000000..4a53bee
--- /dev/null
+++ b/.idea/AndroidProjectSystem.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..7643783
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..bcf4711
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..ca16a99
--- /dev/null
+++ b/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..cdbc250
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..a7fab1c
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..16660f1
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index c341901..bdc7016 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -43,6 +43,7 @@ dependencies {
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.compose.material3)
+ implementation(libs.androidx.compose.material.icons.extended)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview)
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/AlbumCard.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/AlbumCard.kt
new file mode 100644
index 0000000..66059f6
--- /dev/null
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/AlbumCard.kt
@@ -0,0 +1,55 @@
+package com.InfernalAquatics.deepwave.ui.components
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import android.content.res.Configuration
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme
+
+private val AlbumCardWidth = 140.dp
+
+@Composable
+fun AlbumCard(
+ title: String,
+ subtitle: String,
+ modifier: Modifier = Modifier,
+ onClick: () -> Unit = {},
+) {
+ Column(
+ modifier = modifier
+ .width(AlbumCardWidth)
+ .clickable(onClick = onClick),
+ ) {
+ ArtworkPlaceholder(size = AlbumCardWidth)
+ Text(
+ text = title,
+ style = MaterialTheme.typography.bodyMedium,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier.padding(top = 8.dp),
+ )
+ Text(
+ text = subtitle,
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+ }
+}
+
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun AlbumCardPreview() {
+ DeepwaveTheme {
+ AlbumCard(title = "Sample Album", subtitle = "Sample Artist")
+ }
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/ArtistCard.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/ArtistCard.kt
new file mode 100644
index 0000000..4a50224
--- /dev/null
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/ArtistCard.kt
@@ -0,0 +1,51 @@
+package com.InfernalAquatics.deepwave.ui.components
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import android.content.res.Configuration
+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.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme
+
+private val ArtistCardWidth = 110.dp
+
+@Composable
+fun ArtistCard(
+ name: String,
+ modifier: Modifier = Modifier,
+ onClick: () -> Unit = {},
+) {
+ Column(
+ modifier = modifier
+ .width(ArtistCardWidth)
+ .clickable(onClick = onClick),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ ArtworkPlaceholder(size = ArtistCardWidth, circular = true)
+ Text(
+ text = name,
+ style = MaterialTheme.typography.bodyMedium,
+ textAlign = TextAlign.Center,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ modifier = Modifier.padding(top = 8.dp),
+ )
+ }
+}
+
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun ArtistCardPreview() {
+ DeepwaveTheme {
+ ArtistCard(name = "Sample Artist")
+ }
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/ArtworkPlaceholder.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/ArtworkPlaceholder.kt
new file mode 100644
index 0000000..af05f03
--- /dev/null
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/ArtworkPlaceholder.kt
@@ -0,0 +1,56 @@
+package com.InfernalAquatics.deepwave.ui.components
+
+import android.content.res.Configuration
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.MusicNote
+import androidx.compose.material.icons.filled.Person
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme
+
+/** Cover-art placeholder shown until Phase 3 wires in real artwork via Coil. */
+@Composable
+fun ArtworkPlaceholder(
+ size: Dp,
+ modifier: Modifier = Modifier,
+ circular: Boolean = false,
+) {
+ Box(
+ modifier = modifier
+ .size(size)
+ .clip(if (circular) CircleShape else RoundedCornerShape(8.dp))
+ .background(MaterialTheme.colorScheme.surfaceVariant),
+ contentAlignment = Alignment.Center,
+ ) {
+ Icon(
+ imageVector = if (circular) Icons.Filled.Person else Icons.Filled.MusicNote,
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ }
+}
+
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun ArtworkPlaceholderPreview() {
+ DeepwaveTheme {
+ Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
+ ArtworkPlaceholder(size = 96.dp)
+ ArtworkPlaceholder(size = 96.dp, circular = true)
+ }
+ }
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/BottomNavBar.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/BottomNavBar.kt
new file mode 100644
index 0000000..e276002
--- /dev/null
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/BottomNavBar.kt
@@ -0,0 +1,52 @@
+package com.InfernalAquatics.deepwave.ui.components
+
+import android.content.res.Configuration
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Home
+import androidx.compose.material.icons.filled.LibraryMusic
+import androidx.compose.material.icons.filled.Search
+import androidx.compose.material3.Icon
+import androidx.compose.material3.NavigationBar
+import androidx.compose.material3.NavigationBarItem
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.InfernalAquatics.deepwave.R
+import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme
+
+enum class BottomTab(val labelRes: Int, val icon: ImageVector) {
+ Home(R.string.nav_home, Icons.Filled.Home),
+ Search(R.string.nav_search, Icons.Filled.Search),
+ Library(R.string.nav_library, Icons.Filled.LibraryMusic),
+}
+
+@Composable
+fun BottomNavBar(selectedTab: BottomTab, onTabSelected: (BottomTab) -> Unit) {
+ NavigationBar {
+ BottomTab.entries.forEach { tab ->
+ val label = stringResource(tab.labelRes)
+ NavigationBarItem(
+ selected = tab == selectedTab,
+ onClick = { onTabSelected(tab) },
+ icon = { Icon(tab.icon, contentDescription = label) },
+ label = { Text(label) },
+ )
+ }
+ }
+}
+
+/** Interactive in Android Studio's preview pane — tap a tab to see the selection change. */
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun BottomNavBarPreview() {
+ DeepwaveTheme {
+ var selected by remember { mutableStateOf(BottomTab.Home) }
+ BottomNavBar(selectedTab = selected, onTabSelected = { selected = it })
+ }
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/MiniPlayerBar.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/MiniPlayerBar.kt
new file mode 100644
index 0000000..e609d90
--- /dev/null
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/MiniPlayerBar.kt
@@ -0,0 +1,53 @@
+package com.InfernalAquatics.deepwave.ui.components
+
+import android.content.res.Configuration
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.PlayArrow
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+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.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.InfernalAquatics.deepwave.R
+import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme
+
+/** Placeholder now-playing bar; wired to real PlaybackController state in Phase 4. */
+@Composable
+fun MiniPlayerBar(modifier: Modifier = Modifier) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .background(MaterialTheme.colorScheme.surfaceVariant)
+ .padding(horizontal = 16.dp, vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ ArtworkPlaceholder(size = 40.dp)
+ Text(
+ text = stringResource(R.string.player_nothing_playing),
+ style = MaterialTheme.typography.bodyMedium,
+ modifier = Modifier
+ .weight(1f)
+ .padding(start = 12.dp),
+ )
+ IconButton(onClick = {}, enabled = false) {
+ Icon(Icons.Filled.PlayArrow, contentDescription = stringResource(R.string.player_play))
+ }
+ }
+}
+
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun MiniPlayerBarPreview() {
+ DeepwaveTheme {
+ MiniPlayerBar()
+ }
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/TrackRow.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/TrackRow.kt
new file mode 100644
index 0000000..a3a8358
--- /dev/null
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/components/TrackRow.kt
@@ -0,0 +1,65 @@
+package com.InfernalAquatics.deepwave.ui.components
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import android.content.res.Configuration
+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.text.style.TextOverflow
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme
+
+@Composable
+fun TrackRow(
+ title: String,
+ subtitle: String,
+ modifier: Modifier = Modifier,
+ trailingContent: (@Composable RowScope.() -> Unit)? = null,
+ onClick: () -> Unit = {},
+) {
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .clickable(onClick = onClick)
+ .padding(horizontal = 16.dp, vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ ArtworkPlaceholder(size = 48.dp)
+ Column(
+ modifier = Modifier
+ .weight(1f)
+ .padding(start = 12.dp),
+ ) {
+ Text(
+ text = title,
+ style = MaterialTheme.typography.bodyLarge,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+ Text(
+ text = subtitle,
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+ }
+ trailingContent?.invoke(this)
+ }
+}
+
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun TrackRowPreview() {
+ DeepwaveTheme {
+ TrackRow(title = "Sample Track", subtitle = "Sample Artist")
+ }
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/home/HomeScreen.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/home/HomeScreen.kt
index 425eaba..1b39e05 100644
--- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/home/HomeScreen.kt
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/home/HomeScreen.kt
@@ -2,36 +2,67 @@ package com.InfernalAquatics.deepwave.ui.home
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Button
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import android.content.res.Configuration
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.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
+import com.InfernalAquatics.deepwave.R
+import com.InfernalAquatics.deepwave.ui.components.AlbumCard
+import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme
-/** Placeholder post-login landing screen; replaced by the library browse UI in Phase 2. */
+/** Sample content demonstrating the design system; replaced by real library data in Phase 3. */
@Composable
-fun HomeScreen(
- onLoggedOut: () -> Unit,
- viewModel: HomeViewModel = hiltViewModel(),
-) {
+fun HomeScreen(modifier: Modifier = Modifier) {
Column(
- modifier = Modifier
+ modifier = modifier
.fillMaxSize()
- .padding(24.dp),
- verticalArrangement = Arrangement.Center,
- horizontalAlignment = Alignment.CenterHorizontally,
+ .verticalScroll(rememberScrollState())
+ .padding(vertical = 16.dp),
+ verticalArrangement = Arrangement.spacedBy(24.dp),
) {
- Text(text = "You're connected", style = MaterialTheme.typography.headlineSmall)
- Spacer(modifier = Modifier.height(24.dp))
- Button(onClick = { viewModel.logout(onLoggedOut) }) {
- Text("Log out")
+ HomeRow(title = stringResource(R.string.home_row_recently_added))
+ HomeRow(title = stringResource(R.string.home_row_recently_played))
+ HomeRow(title = stringResource(R.string.home_row_made_for_you))
+ }
+}
+
+@Composable
+private fun HomeRow(title: String) {
+ Column {
+ Text(
+ text = title,
+ style = MaterialTheme.typography.titleLarge,
+ modifier = Modifier.padding(horizontal = 16.dp),
+ )
+ Spacer(modifier = Modifier.height(12.dp))
+ LazyRow(
+ contentPadding = PaddingValues(horizontal = 16.dp),
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ items(5) { index ->
+ AlbumCard(title = "Sample Album ${index + 1}", subtitle = "Sample Artist")
+ }
}
}
}
+
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 411, heightDp = 891)
+@Composable
+private fun HomeScreenPreview() {
+ DeepwaveTheme {
+ HomeScreen()
+ }
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/library/LibraryScreen.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/library/LibraryScreen.kt
new file mode 100644
index 0000000..a8546ed
--- /dev/null
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/library/LibraryScreen.kt
@@ -0,0 +1,30 @@
+package com.InfernalAquatics.deepwave.ui.library
+
+import android.content.res.Configuration
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+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.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.InfernalAquatics.deepwave.R
+import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme
+
+/** Placeholder; real library browsing (artists/albums) arrives in Phase 3. */
+@Composable
+fun LibraryScreen(modifier: Modifier = Modifier) {
+ Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+ Text(text = stringResource(R.string.library_placeholder), style = MaterialTheme.typography.bodyLarge)
+ }
+}
+
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 411, heightDp = 891)
+@Composable
+private fun LibraryScreenPreview() {
+ DeepwaveTheme {
+ LibraryScreen()
+ }
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/login/LoginScreen.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/login/LoginScreen.kt
index 214ff3c..717ca16 100644
--- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/login/LoginScreen.kt
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/login/LoginScreen.kt
@@ -1,5 +1,6 @@
package com.InfernalAquatics.deepwave.ui.login
+import android.content.res.Configuration
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
@@ -19,10 +20,14 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
+import com.InfernalAquatics.deepwave.R
+import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme
@Composable
fun LoginScreen(
@@ -35,6 +40,23 @@ fun LoginScreen(
if (uiState.loginSucceeded) onLoginSuccess()
}
+ LoginContent(
+ uiState = uiState,
+ onServerUrlChange = viewModel::onServerUrlChange,
+ onUsernameChange = viewModel::onUsernameChange,
+ onPasswordChange = viewModel::onPasswordChange,
+ onLoginClick = viewModel::login,
+ )
+}
+
+@Composable
+private fun LoginContent(
+ uiState: LoginUiState,
+ onServerUrlChange: (String) -> Unit,
+ onUsernameChange: (String) -> Unit,
+ onPasswordChange: (String) -> Unit,
+ onLoginClick: () -> Unit,
+) {
Column(
modifier = Modifier
.fillMaxSize()
@@ -42,16 +64,16 @@ fun LoginScreen(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
- Text(text = "Deepwave", style = MaterialTheme.typography.headlineMedium)
- Text(text = "Connect to your Navidrome server", style = MaterialTheme.typography.bodyMedium)
+ Text(text = stringResource(R.string.login_title), style = MaterialTheme.typography.headlineMedium)
+ Text(text = stringResource(R.string.login_subtitle), style = MaterialTheme.typography.bodyMedium)
Spacer(modifier = Modifier.height(24.dp))
OutlinedTextField(
value = uiState.serverUrl,
- onValueChange = viewModel::onServerUrlChange,
- label = { Text("Server URL") },
- placeholder = { Text("https://navidrome.example.com") },
+ onValueChange = onServerUrlChange,
+ label = { Text(stringResource(R.string.login_server_url_label)) },
+ placeholder = { Text(stringResource(R.string.login_server_url_placeholder)) },
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri),
modifier = Modifier.fillMaxWidth(),
@@ -61,8 +83,8 @@ fun LoginScreen(
OutlinedTextField(
value = uiState.username,
- onValueChange = viewModel::onUsernameChange,
- label = { Text("Username") },
+ onValueChange = onUsernameChange,
+ label = { Text(stringResource(R.string.login_username_label)) },
singleLine = true,
modifier = Modifier.fillMaxWidth(),
)
@@ -71,8 +93,8 @@ fun LoginScreen(
OutlinedTextField(
value = uiState.password,
- onValueChange = viewModel::onPasswordChange,
- label = { Text("Password") },
+ onValueChange = onPasswordChange,
+ label = { Text(stringResource(R.string.login_password_label)) },
singleLine = true,
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
@@ -93,9 +115,59 @@ fun LoginScreen(
if (uiState.isLoading) {
CircularProgressIndicator()
} else {
- Button(onClick = viewModel::login, modifier = Modifier.fillMaxWidth()) {
- Text("Log in")
+ Button(onClick = onLoginClick, modifier = Modifier.fillMaxWidth()) {
+ Text(stringResource(R.string.login_button))
}
}
}
}
+
+@Preview(name = "Empty", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 411, heightDp = 891)
+@Composable
+private fun LoginContentEmptyPreview() {
+ DeepwaveTheme {
+ LoginContent(
+ uiState = LoginUiState(),
+ onServerUrlChange = {},
+ onUsernameChange = {},
+ onPasswordChange = {},
+ onLoginClick = {},
+ )
+ }
+}
+
+@Preview(name = "Loading", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 411, heightDp = 891)
+@Composable
+private fun LoginContentLoadingPreview() {
+ DeepwaveTheme {
+ LoginContent(
+ uiState = LoginUiState(
+ serverUrl = "https://navidrome.example.com",
+ username = "alex",
+ isLoading = true,
+ ),
+ onServerUrlChange = {},
+ onUsernameChange = {},
+ onPasswordChange = {},
+ onLoginClick = {},
+ )
+ }
+}
+
+@Preview(name = "Error", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 411, heightDp = 891)
+@Composable
+private fun LoginContentErrorPreview() {
+ DeepwaveTheme {
+ LoginContent(
+ uiState = LoginUiState(
+ serverUrl = "https://navidrome.example.com",
+ username = "alex",
+ errorMessage = "Couldn't reach server: timeout",
+ ),
+ onServerUrlChange = {},
+ onUsernameChange = {},
+ onPasswordChange = {},
+ onLoginClick = {},
+ )
+ }
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/DeepwaveNavHost.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/DeepwaveNavHost.kt
index 2f32e30..22d41f7 100644
--- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/DeepwaveNavHost.kt
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/DeepwaveNavHost.kt
@@ -13,13 +13,8 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
-import com.InfernalAquatics.deepwave.ui.home.HomeScreen
import com.InfernalAquatics.deepwave.ui.login.LoginScreen
-
-private object Routes {
- const val LOGIN = "login"
- const val HOME = "home"
-}
+import com.InfernalAquatics.deepwave.ui.settings.SettingsScreen
@Composable
fun DeepwaveNavHost(
@@ -34,22 +29,26 @@ fun DeepwaveNavHost(
}
else -> NavHost(
navController = navController,
- startDestination = if (isLoggedIn == true) Routes.HOME else Routes.LOGIN,
+ startDestination = if (isLoggedIn == true) Route.Main else Route.Login,
) {
- composable(Routes.LOGIN) {
+ composable {
LoginScreen(
onLoginSuccess = {
- navController.navigate(Routes.HOME) {
- popUpTo(Routes.LOGIN) { inclusive = true }
+ navController.navigate(Route.Main) {
+ popUpTo { inclusive = true }
}
},
)
}
- composable(Routes.HOME) {
- HomeScreen(
+ composable {
+ MainScreen(onOpenSettings = { navController.navigate(Route.Settings) })
+ }
+ composable {
+ SettingsScreen(
+ onBack = { navController.popBackStack() },
onLoggedOut = {
- navController.navigate(Routes.LOGIN) {
- popUpTo(Routes.HOME) { inclusive = true }
+ navController.navigate(Route.Login) {
+ popUpTo { inclusive = true }
}
},
)
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/MainScreen.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/MainScreen.kt
new file mode 100644
index 0000000..112b648
--- /dev/null
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/MainScreen.kt
@@ -0,0 +1,78 @@
+package com.InfernalAquatics.deepwave.ui.navigation
+
+import android.content.res.Configuration
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Scaffold
+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
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.InfernalAquatics.deepwave.R
+import com.InfernalAquatics.deepwave.ui.components.BottomNavBar
+import com.InfernalAquatics.deepwave.ui.components.BottomTab
+import com.InfernalAquatics.deepwave.ui.components.MiniPlayerBar
+import com.InfernalAquatics.deepwave.ui.home.HomeScreen
+import com.InfernalAquatics.deepwave.ui.library.LibraryScreen
+import com.InfernalAquatics.deepwave.ui.search.SearchScreen
+import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme
+
+/** Hosts the bottom-tab shell (Home / Search / Library) plus the mini-player bar and settings entry point. */
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MainScreen(onOpenSettings: () -> Unit) {
+ var selectedTab by remember { mutableStateOf(BottomTab.Home) }
+
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ title = { Text(stringResource(R.string.app_name)) },
+ actions = {
+ IconButton(onClick = onOpenSettings) {
+ Icon(Icons.Filled.Settings, contentDescription = stringResource(R.string.action_settings))
+ }
+ },
+ )
+ },
+ bottomBar = {
+ Column {
+ MiniPlayerBar()
+ BottomNavBar(selectedTab = selectedTab, onTabSelected = { selectedTab = it })
+ }
+ },
+ ) { innerPadding ->
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(innerPadding),
+ ) {
+ when (selectedTab) {
+ BottomTab.Home -> HomeScreen()
+ BottomTab.Search -> SearchScreen()
+ BottomTab.Library -> LibraryScreen()
+ }
+ }
+ }
+}
+
+/** Interactive in Android Studio's preview pane — try tapping the tabs and the settings icon. */
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 411, heightDp = 891)
+@Composable
+private fun MainScreenPreview() {
+ DeepwaveTheme {
+ MainScreen(onOpenSettings = {})
+ }
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/Route.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/Route.kt
new file mode 100644
index 0000000..2ddc217
--- /dev/null
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/Route.kt
@@ -0,0 +1,9 @@
+package com.InfernalAquatics.deepwave.ui.navigation
+
+import kotlinx.serialization.Serializable
+
+sealed interface Route {
+ @Serializable data object Login : Route
+ @Serializable data object Main : Route
+ @Serializable data object Settings : Route
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/search/SearchScreen.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/search/SearchScreen.kt
new file mode 100644
index 0000000..c87b14b
--- /dev/null
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/search/SearchScreen.kt
@@ -0,0 +1,30 @@
+package com.InfernalAquatics.deepwave.ui.search
+
+import android.content.res.Configuration
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+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.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.InfernalAquatics.deepwave.R
+import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme
+
+/** Placeholder; real search UI arrives in Phase 3. */
+@Composable
+fun SearchScreen(modifier: Modifier = Modifier) {
+ Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+ Text(text = stringResource(R.string.search_placeholder), style = MaterialTheme.typography.bodyLarge)
+ }
+}
+
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 411, heightDp = 891)
+@Composable
+private fun SearchScreenPreview() {
+ DeepwaveTheme {
+ SearchScreen()
+ }
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/settings/SettingsScreen.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/settings/SettingsScreen.kt
new file mode 100644
index 0000000..4fa385a
--- /dev/null
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/settings/SettingsScreen.kt
@@ -0,0 +1,71 @@
+package com.InfernalAquatics.deepwave.ui.settings
+
+import android.content.res.Configuration
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.Button
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
+import com.InfernalAquatics.deepwave.R
+import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme
+
+@Composable
+fun SettingsScreen(
+ onBack: () -> Unit,
+ onLoggedOut: () -> Unit,
+ viewModel: SettingsViewModel = hiltViewModel(),
+) {
+ SettingsContent(
+ onBack = onBack,
+ onLogOutClick = { viewModel.logout(onLoggedOut) },
+ )
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun SettingsContent(onBack: () -> Unit, onLogOutClick: () -> Unit) {
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ title = { Text(stringResource(R.string.settings_title)) },
+ navigationIcon = {
+ IconButton(onClick = onBack) {
+ Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.action_back))
+ }
+ },
+ )
+ },
+ ) { innerPadding ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(innerPadding)
+ .padding(16.dp),
+ ) {
+ Button(onClick = onLogOutClick) {
+ Text(stringResource(R.string.settings_log_out))
+ }
+ }
+ }
+}
+
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 411, heightDp = 891)
+@Composable
+private fun SettingsContentPreview() {
+ DeepwaveTheme {
+ SettingsContent(onBack = {}, onLogOutClick = {})
+ }
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/home/HomeViewModel.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/settings/SettingsViewModel.kt
similarity index 84%
rename from app/src/main/java/com/InfernalAquatics/deepwave/ui/home/HomeViewModel.kt
rename to app/src/main/java/com/InfernalAquatics/deepwave/ui/settings/SettingsViewModel.kt
index adbd3ce..316902e 100644
--- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/home/HomeViewModel.kt
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/settings/SettingsViewModel.kt
@@ -1,4 +1,4 @@
-package com.InfernalAquatics.deepwave.ui.home
+package com.InfernalAquatics.deepwave.ui.settings
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
@@ -8,7 +8,7 @@ import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
-class HomeViewModel @Inject constructor(
+class SettingsViewModel @Inject constructor(
private val serverRepository: ServerRepository,
) : ViewModel() {
fun logout(onLoggedOut: () -> Unit) {
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Color.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Color.kt
index 8b8a91e..ee49243 100644
--- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Color.kt
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Color.kt
@@ -2,10 +2,27 @@ package com.InfernalAquatics.deepwave.ui.theme
import androidx.compose.ui.graphics.Color
-val Purple80 = Color(0xFFD0BCFF)
-val PurpleGrey80 = Color(0xFFCCC2DC)
-val Pink80 = Color(0xFFEFB8C8)
+// Dark palette (primary) — near-black "deep water" surfaces with an aqua accent.
+val DeepwaveBackgroundDark = Color(0xFF0B0F14)
+val DeepwaveSurfaceDark = Color(0xFF12181F)
+val DeepwaveSurfaceVariantDark = Color(0xFF1E2830)
+val DeepwaveOnBackgroundDark = Color(0xFFECEFF2)
+val DeepwaveOnSurfaceVariantDark = Color(0xFFA9B4BD)
-val Purple40 = Color(0xFF6650a4)
-val PurpleGrey40 = Color(0xFF625b71)
-val Pink40 = Color(0xFF7D5260)
\ No newline at end of file
+val WaveAqua = Color(0xFF2FE0C7)
+val WaveAquaDeep = Color(0xFF0F8C7A)
+val OnWaveAqua = Color(0xFF00332C)
+
+val DeepwaveSecondaryDark = Color(0xFF4A6572)
+val OnDeepwaveSecondaryDark = Color(0xFFE3E9EC)
+
+val DeepwaveError = Color(0xFFFF5449)
+val OnDeepwaveError = Color(0xFF1A0000)
+
+// Light palette (secondary, system-light-mode fallback)
+val DeepwaveBackgroundLight = Color(0xFFF7F9FA)
+val DeepwaveSurfaceLight = Color(0xFFFFFFFF)
+val DeepwaveSurfaceVariantLight = Color(0xFFE3E8EA)
+val DeepwaveOnBackgroundLight = Color(0xFF12181F)
+val DeepwaveOnSurfaceVariantLight = Color(0xFF44505A)
+val DeepwaveSecondaryLight = Color(0xFF3E5461)
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Shape.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Shape.kt
new file mode 100644
index 0000000..04df2aa
--- /dev/null
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Shape.kt
@@ -0,0 +1,13 @@
+package com.InfernalAquatics.deepwave.ui.theme
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Shapes
+import androidx.compose.ui.unit.dp
+
+val Shapes = Shapes(
+ extraSmall = RoundedCornerShape(4.dp),
+ small = RoundedCornerShape(8.dp),
+ medium = RoundedCornerShape(12.dp),
+ large = RoundedCornerShape(16.dp),
+ extraLarge = RoundedCornerShape(28.dp),
+)
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Theme.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Theme.kt
index dc301b4..2d85c9e 100644
--- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Theme.kt
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Theme.kt
@@ -1,58 +1,61 @@
package com.InfernalAquatics.deepwave.ui.theme
-import android.app.Activity
-import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
-import androidx.compose.material3.dynamicDarkColorScheme
-import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
-import androidx.compose.ui.platform.LocalContext
private val DarkColorScheme = darkColorScheme(
- primary = Purple80,
- secondary = PurpleGrey80,
- tertiary = Pink80
+ primary = WaveAqua,
+ onPrimary = OnWaveAqua,
+ primaryContainer = WaveAquaDeep,
+ onPrimaryContainer = DeepwaveOnBackgroundDark,
+ secondary = DeepwaveSecondaryDark,
+ onSecondary = OnDeepwaveSecondaryDark,
+ background = DeepwaveBackgroundDark,
+ onBackground = DeepwaveOnBackgroundDark,
+ surface = DeepwaveSurfaceDark,
+ onSurface = DeepwaveOnBackgroundDark,
+ surfaceVariant = DeepwaveSurfaceVariantDark,
+ onSurfaceVariant = DeepwaveOnSurfaceVariantDark,
+ error = DeepwaveError,
+ onError = OnDeepwaveError,
)
private val LightColorScheme = lightColorScheme(
- primary = Purple40,
- secondary = PurpleGrey40,
- tertiary = Pink40
-
- /* Other default colors to override
- background = Color(0xFFFFFBFE),
- surface = Color(0xFFFFFBFE),
- onPrimary = Color.White,
- onSecondary = Color.White,
- onTertiary = Color.White,
- onBackground = Color(0xFF1C1B1F),
- onSurface = Color(0xFF1C1B1F),
- */
+ primary = WaveAquaDeep,
+ onPrimary = DeepwaveSurfaceLight,
+ primaryContainer = WaveAqua,
+ onPrimaryContainer = OnWaveAqua,
+ secondary = DeepwaveSecondaryLight,
+ onSecondary = DeepwaveSurfaceLight,
+ background = DeepwaveBackgroundLight,
+ onBackground = DeepwaveOnBackgroundLight,
+ surface = DeepwaveSurfaceLight,
+ onSurface = DeepwaveOnBackgroundLight,
+ surfaceVariant = DeepwaveSurfaceVariantLight,
+ onSurfaceVariant = DeepwaveOnSurfaceVariantLight,
+ error = DeepwaveError,
+ onError = OnDeepwaveError,
)
+/**
+ * Deepwave's brand theme: dark-first with a fixed aqua accent. Deliberately not
+ * Material You dynamic color, so the app keeps a consistent identity regardless
+ * of the user's wallpaper, matching the Spotify/SoundCloud-style brief.
+ */
@Composable
fun DeepwaveTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
- // Dynamic color is available on Android 12+
- dynamicColor: Boolean = true,
- content: @Composable () -> Unit
+ content: @Composable () -> Unit,
) {
- val colorScheme = when {
- dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
- val context = LocalContext.current
- if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
- }
-
- darkTheme -> DarkColorScheme
- else -> LightColorScheme
- }
+ val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
- content = content
+ shapes = Shapes,
+ content = content,
)
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Type.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Type.kt
index 611bc05..baec026 100644
--- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Type.kt
+++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/theme/Type.kt
@@ -6,29 +6,20 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
-// Set of Material typography styles to start with
val Typography = Typography(
- bodyLarge = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Normal,
- fontSize = 16.sp,
- lineHeight = 24.sp,
- letterSpacing = 0.5.sp
- )
- /* Other default text styles to override
- titleLarge = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Normal,
- fontSize = 22.sp,
- lineHeight = 28.sp,
- letterSpacing = 0.sp
- ),
- labelSmall = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Medium,
- fontSize = 11.sp,
- lineHeight = 16.sp,
- letterSpacing = 0.5.sp
- )
- */
-)
\ No newline at end of file
+ displayLarge = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.Bold, fontSize = 57.sp, lineHeight = 64.sp, letterSpacing = (-0.25).sp),
+ displayMedium = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.Bold, fontSize = 45.sp, lineHeight = 52.sp, letterSpacing = 0.sp),
+ displaySmall = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.Bold, fontSize = 36.sp, lineHeight = 44.sp, letterSpacing = 0.sp),
+ headlineLarge = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.Bold, fontSize = 32.sp, lineHeight = 40.sp, letterSpacing = 0.sp),
+ headlineMedium = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.Bold, fontSize = 28.sp, lineHeight = 36.sp, letterSpacing = 0.sp),
+ headlineSmall = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.Bold, fontSize = 24.sp, lineHeight = 32.sp, letterSpacing = 0.sp),
+ titleLarge = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.SemiBold, fontSize = 22.sp, lineHeight = 28.sp, letterSpacing = 0.sp),
+ titleMedium = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.SemiBold, fontSize = 16.sp, lineHeight = 24.sp, letterSpacing = 0.15.sp),
+ titleSmall = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.Medium, fontSize = 14.sp, lineHeight = 20.sp, letterSpacing = 0.1.sp),
+ bodyLarge = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.Normal, fontSize = 16.sp, lineHeight = 24.sp, letterSpacing = 0.5.sp),
+ bodyMedium = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.Normal, fontSize = 14.sp, lineHeight = 20.sp, letterSpacing = 0.25.sp),
+ bodySmall = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.Normal, fontSize = 12.sp, lineHeight = 16.sp, letterSpacing = 0.4.sp),
+ labelLarge = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.Medium, fontSize = 14.sp, lineHeight = 20.sp, letterSpacing = 0.1.sp),
+ labelMedium = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.Medium, fontSize = 12.sp, lineHeight = 16.sp, letterSpacing = 0.5.sp),
+ labelSmall = TextStyle(fontFamily = FontFamily.Default, fontWeight = FontWeight.Medium, fontSize = 11.sp, lineHeight = 16.sp, letterSpacing = 0.5.sp),
+)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c6afdbe..94d668c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,30 @@
Deepwave
-
\ No newline at end of file
+
+ Deepwave
+ Connect to your Navidrome server
+ Server URL
+ https://navidrome.example.com
+ Username
+ Password
+ Log in
+
+ Home
+ Search
+ Library
+ Settings
+ Back
+
+ Settings
+ Log out
+
+ Search coming soon
+ Your library coming soon
+
+ Nothing playing
+ Play
+
+ Recently Added
+ Recently Played
+ Made For You
+
diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml
index 4df9255..c2913d3 100644
--- a/app/src/main/res/xml/backup_rules.xml
+++ b/app/src/main/res/xml/backup_rules.xml
@@ -6,8 +6,6 @@
See https://developer.android.com/about/versions/12/backup-restore
-->
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml
index 9ee9997..df2fd3f 100644
--- a/app/src/main/res/xml/data_extraction_rules.xml
+++ b/app/src/main/res/xml/data_extraction_rules.xml
@@ -5,15 +5,11 @@
-->
-
+
+
-
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 6f3c958..193b31d 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -34,6 +34,7 @@ androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "u
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
+androidx-compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hiltAndroid" }
diff --git a/gradlew b/gradlew
old mode 100644
new mode 100755