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 @@ + + + + + + + + + + \ 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