From e1d18fcc3537618d8dc965ecbe5ed5a735cc1cb0 Mon Sep 17 00:00:00 2001 From: grakovne Date: Fri, 1 Nov 2024 19:59:56 +0100 Subject: [PATCH 01/59] wip --- app/build.gradle.kts | 6 +-- .../lissen/ui/navigation/AppNavHost.kt | 2 +- .../ui/screens/settings/SettingsScreen.kt | 2 + .../AdvancedSettingsItemComposable.kt | 51 +++++++++++++++++++ 4 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/AdvancedSettingsItemComposable.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fb1ad5f4..67305abe 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -25,8 +25,8 @@ android { applicationId = "org.grakovne.lissen" minSdk = 28 targetSdk = 35 - versionCode = 13 - versionName = "1.0.12" + versionCode = 14 + versionName = "1.0.13-dev" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { @@ -39,7 +39,7 @@ android { } debug { matchingFallbacks.add("release") - isDebuggable = false + isDebuggable = true } } diff --git a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt index a1948b2e..9bd5eafa 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt @@ -34,7 +34,7 @@ fun AppNavHost( ) } val startDestination = when { - hasCredentials -> "library_screen" + hasCredentials -> "settings_screen" else -> "login_screen" } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt index 0621987a..a6b834ff 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt @@ -29,6 +29,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import org.grakovne.lissen.R import org.grakovne.lissen.ui.navigation.AppNavigationService import org.grakovne.lissen.ui.screens.settings.composable.AdditionalComposable +import org.grakovne.lissen.ui.screens.settings.composable.AdvancedSettingsItemComposable import org.grakovne.lissen.ui.screens.settings.composable.GeneralSettingsComposable import org.grakovne.lissen.ui.screens.settings.composable.ServerComposable import org.grakovne.lissen.viewmodel.SettingsViewModel @@ -86,6 +87,7 @@ fun SettingsScreen( ) { ServerComposable(navController, viewModel) GeneralSettingsComposable(viewModel) + AdvancedSettingsItemComposable("Custom Headers", "Define headers for each app request") {} } AdditionalComposable() } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/AdvancedSettingsItemComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/AdvancedSettingsItemComposable.kt new file mode 100644 index 00000000..1f6ee4a9 --- /dev/null +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/AdvancedSettingsItemComposable.kt @@ -0,0 +1,51 @@ +package org.grakovne.lissen.ui.screens.settings.composable + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +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.automirrored.filled.ArrowForward +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme.colorScheme +import androidx.compose.material3.MaterialTheme.typography +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.font.FontWeight +import androidx.compose.ui.unit.dp + +@Composable +fun AdvancedSettingsItemComposable( + title: String, + description: String, + onclick: () -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { onclick() } + .padding(horizontal = 16.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Column(modifier = Modifier.weight(1f)) { + Text( + text = title, + style = typography.bodyLarge.copy(fontWeight = FontWeight.SemiBold), + modifier = Modifier.padding(bottom = 4.dp) + ) + Text( + text = description, + style = typography.bodyMedium, + color = colorScheme.onSurfaceVariant.copy(alpha = 0.6f) + ) + } + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowForward, + contentDescription = "Arrow Icon", + tint = colorScheme.onSurfaceVariant + ) + } +} From b906cb48e12e082eceafdf6389b6e06b11a447c7 Mon Sep 17 00:00:00 2001 From: grakovne Date: Fri, 1 Nov 2024 21:06:37 +0100 Subject: [PATCH 02/59] wip --- .../lissen/ui/navigation/AppNavHost.kt | 7 +- .../ui/navigation/AppNavigationService.kt | 4 + .../ui/screens/settings/SettingsScreen.kt | 6 +- .../advanced/CustomHeadersSettingsScreen.kt | 104 ++++++++++++++++++ 4 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt diff --git a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt index 9bd5eafa..bbd96662 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt @@ -19,6 +19,7 @@ import org.grakovne.lissen.ui.screens.library.LibraryScreen import org.grakovne.lissen.ui.screens.login.LoginScreen import org.grakovne.lissen.ui.screens.player.PlayerScreen import org.grakovne.lissen.ui.screens.settings.SettingsScreen +import org.grakovne.lissen.ui.screens.settings.advanced.CustomHeadersSettingsScreen @Composable fun AppNavHost( @@ -34,7 +35,7 @@ fun AppNavHost( ) } val startDestination = when { - hasCredentials -> "settings_screen" + hasCredentials -> "settings_screen/custom_headers" else -> "login_screen" } @@ -81,6 +82,10 @@ fun AppNavHost( navController = navigationService ) } + + composable("settings_screen/custom_headers") { + CustomHeadersSettingsScreen() + } } } } diff --git a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavigationService.kt b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavigationService.kt index 92814646..c53c04f3 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavigationService.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavigationService.kt @@ -28,6 +28,10 @@ class AppNavigationService( host.navigate("settings_screen") } + fun showCustomHeadersSettings() { + host.navigate("settings_screen/custom_headers") + } + fun showLogin() { host.navigate("login_screen") { popUpTo(0) { diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt index a6b834ff..5b1efbf5 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt @@ -87,7 +87,11 @@ fun SettingsScreen( ) { ServerComposable(navController, viewModel) GeneralSettingsComposable(viewModel) - AdvancedSettingsItemComposable("Custom Headers", "Define headers for each app request") {} + AdvancedSettingsItemComposable( + title = "Custom Headers", + description = "Define headers for each app request", + onclick = { navController.showCustomHeadersSettings() } + ) } AdditionalComposable() } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt new file mode 100644 index 00000000..de916993 --- /dev/null +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -0,0 +1,104 @@ +package org.grakovne.lissen.ui.screens.settings.advanced + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +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.automirrored.outlined.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.MaterialTheme.colorScheme +import androidx.compose.material3.MaterialTheme.typography +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CustomHeadersSettingsScreen() { + Scaffold( + topBar = { + TopAppBar( + title = { + Text( + text = "Custom Headers", + style = typography.titleLarge.copy(fontWeight = FontWeight.SemiBold), + color = colorScheme.onSurface + ) + }, + navigationIcon = { + IconButton(onClick = { }) { + Icon( + imageVector = Icons.AutoMirrored.Outlined.ArrowBack, + contentDescription = "Back", + tint = colorScheme.onSurface + ) + } + } + ) + }, + modifier = Modifier, + content = { innerPadding -> + Row( + modifier = Modifier + .fillMaxWidth() + .padding(innerPadding), + verticalAlignment = Alignment.CenterVertically, + ) { + Column( + modifier = Modifier + .weight(1f) + .padding(end = 16.dp) + ) { + + OutlinedTextField( + value = "", + onValueChange = {}, + label = { Text("Name") }, + shape = RoundedCornerShape(16.dp), + singleLine = true, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp) + ) + OutlinedTextField( + value = "", + onValueChange = {}, + label = { Text("Name") }, + shape = RoundedCornerShape(16.dp), + singleLine = true, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp) + ) + + } + + Button( + onClick = { }, + shape = CircleShape, + modifier = Modifier.size(48.dp), + contentPadding = PaddingValues(0.dp) + ) { + Text( + text = "+", + style = typography.titleLarge + ) + } + } + + }) +} From 9bae398e4c08836a6edfbae0ca860ac76bb2878b Mon Sep 17 00:00:00 2001 From: grakovne Date: Fri, 1 Nov 2024 22:01:56 +0100 Subject: [PATCH 03/59] wip --- .../advanced/CustomHeadersSettingsScreen.kt | 127 ++++++++++++------ 1 file changed, 85 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index de916993..80e5eb99 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -3,13 +3,18 @@ package org.grakovne.lissen.ui.screens.settings.advanced import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowBack +import androidx.compose.material.icons.filled.Info import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -18,13 +23,16 @@ import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -50,55 +58,90 @@ fun CustomHeadersSettingsScreen() { } ) }, - modifier = Modifier, + modifier = Modifier + .systemBarsPadding() + .fillMaxHeight(), content = { innerPadding -> - Row( + Column( modifier = Modifier - .fillMaxWidth() + .fillMaxSize() .padding(innerPadding), - verticalAlignment = Alignment.CenterVertically, + horizontalAlignment = Alignment.CenterHorizontally ) { - Column( + Surface( modifier = Modifier - .weight(1f) - .padding(end = 16.dp) + .fillMaxWidth() + .padding(top = 12.dp, bottom = 12.dp, start = 16.dp, end = (16).dp), + color = colorScheme.primary.copy(alpha = 0.05f), + shape = RoundedCornerShape(16.dp) ) { - - OutlinedTextField( - value = "", - onValueChange = {}, - label = { Text("Name") }, - shape = RoundedCornerShape(16.dp), - singleLine = true, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 4.dp) - ) - OutlinedTextField( - value = "", - onValueChange = {}, - label = { Text("Name") }, - shape = RoundedCornerShape(16.dp), - singleLine = true, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 4.dp) - ) - + Row( + modifier = Modifier.padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Info, + contentDescription = null, + tint = colorScheme.primary + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = "Custom headers will be included in every request", + style = typography.bodyMedium, + color = colorScheme.onBackground, + textAlign = TextAlign.Start, + modifier = Modifier.weight(1f) + ) + } } - Button( - onClick = { }, - shape = CircleShape, - modifier = Modifier.size(48.dp), - contentPadding = PaddingValues(0.dp) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically ) { - Text( - text = "+", - style = typography.titleLarge - ) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.weight(1f) + ) { + OutlinedTextField( + value = "", + onValueChange = {}, + label = { Text("Name") }, + shape = RoundedCornerShape(16.dp), + singleLine = true, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp) + ) + OutlinedTextField( + value = "", + onValueChange = {}, + label = { Text("Value") }, + shape = RoundedCornerShape(16.dp), + singleLine = true, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp) + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Button( + onClick = {}, + contentPadding = PaddingValues(0.dp) + ) { + Text( + fontSize = 24.sp, + text = "+", + style = typography.titleLarge, + textAlign = TextAlign.Center + ) + } + } } } - - }) + } + ) } From 85ecea678791a16d42aa40bdb6e2a1a72d4aefdc Mon Sep 17 00:00:00 2001 From: grakovne Date: Fri, 1 Nov 2024 22:03:51 +0100 Subject: [PATCH 04/59] wip --- .../main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt index bbd96662..aaaa5263 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt @@ -35,7 +35,7 @@ fun AppNavHost( ) } val startDestination = when { - hasCredentials -> "settings_screen/custom_headers" + hasCredentials -> "settings_screen" else -> "login_screen" } From f0895ac2ba8ab7f4f756fcd0d562fcf325913642 Mon Sep 17 00:00:00 2001 From: grakovne Date: Fri, 1 Nov 2024 22:46:10 +0100 Subject: [PATCH 05/59] wip --- .../lissen/ui/navigation/AppNavHost.kt | 2 +- .../lissen/ui/screens/login/LoginScreen.kt | 21 +++++++++++++++++-- .../advanced/CustomHeadersSettingsScreen.kt | 11 +++++----- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt index aaaa5263..0e0758bf 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt @@ -35,7 +35,7 @@ fun AppNavHost( ) } val startDestination = when { - hasCredentials -> "settings_screen" + hasCredentials -> "login_screen" else -> "login_screen" } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt index e347927c..92142273 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt @@ -4,6 +4,8 @@ import android.content.Context import android.util.Log import android.widget.Toast import android.widget.Toast.LENGTH_SHORT +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -16,11 +18,13 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme.colorScheme +import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -35,6 +39,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle @@ -116,7 +121,7 @@ fun LoginScreen( fontSize = 24.sp, fontWeight = FontWeight.SemiBold, letterSpacing = 2.sp, - textAlign = TextAlign.Start + textAlign = TextAlign.Center ), modifier = Modifier .padding(vertical = 32.dp) @@ -179,10 +184,22 @@ fun LoginScreen( }, modifier = Modifier .fillMaxWidth() - .padding(vertical = 32.dp) + .padding(top = 32.dp) ) { Text(text = stringResource(R.string.login_screen_connect_button_text)) } + Button( + onClick = { + navController.showSettings() + }, + modifier = Modifier + .padding(top = 12.dp) + .fillMaxWidth(), + colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent), + border = BorderStroke(1.dp, colorScheme.primary) + ) { + Text(text = "Advanced Settings", color = colorScheme.primary) + } CircularProgressIndicator( color = FoxOrange, diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 80e5eb99..eb2cf027 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowBack import androidx.compose.material.icons.filled.Info +import androidx.compose.material.icons.outlined.Info import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -71,8 +72,8 @@ fun CustomHeadersSettingsScreen() { Surface( modifier = Modifier .fillMaxWidth() - .padding(top = 12.dp, bottom = 12.dp, start = 16.dp, end = (16).dp), - color = colorScheme.primary.copy(alpha = 0.05f), + .padding(top = 12.dp, bottom = 0.dp, start = 16.dp, end = 16.dp), + color = colorScheme.surfaceDim.copy(alpha = 0.1f), shape = RoundedCornerShape(16.dp) ) { Row( @@ -80,9 +81,9 @@ fun CustomHeadersSettingsScreen() { verticalAlignment = Alignment.CenterVertically ) { Icon( - imageVector = Icons.Default.Info, + imageVector = Icons.Outlined.Info, contentDescription = null, - tint = colorScheme.primary + tint = colorScheme.onBackground ) Spacer(modifier = Modifier.width(12.dp)) Text( @@ -134,7 +135,7 @@ fun CustomHeadersSettingsScreen() { ) { Text( fontSize = 24.sp, - text = "+", + text = " + ", style = typography.titleLarge, textAlign = TextAlign.Center ) From af022191ecc79c6f24a94753764ff27b2fe24a6f Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 18:23:37 +0100 Subject: [PATCH 06/59] wip --- .../lissen/ui/screens/login/LoginScreen.kt | 73 +++++++++++++------ 1 file changed, 52 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt index e724460d..a4d24b6d 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt @@ -4,21 +4,24 @@ import android.content.Context import android.util.Log import android.widget.Toast import android.widget.Toast.LENGTH_SHORT -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -39,7 +42,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle @@ -178,27 +180,56 @@ fun LoginScreen( .padding(vertical = 4.dp) ) - Button( - onClick = { - viewModel.login() - }, + Row( modifier = Modifier .fillMaxWidth() .padding(top = 32.dp) ) { - Text(text = stringResource(R.string.login_screen_connect_button_text)) - } - Button( - onClick = { - navController.showSettings() - }, - modifier = Modifier - .padding(top = 12.dp) - .fillMaxWidth(), - colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent), - border = BorderStroke(1.dp, colorScheme.primary) - ) { - Text(text = "Advanced Settings", color = colorScheme.primary) + Button( + onClick = { + viewModel.login() + }, + modifier = Modifier.weight(1f), + shape = RoundedCornerShape( + topStart = 16.dp, + bottomStart = 16.dp, + topEnd = 0.dp, + bottomEnd = 0.dp + ) + ) { + Spacer(modifier = Modifier.width(28.dp)) + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + Text( + text = stringResource(R.string.login_screen_connect_button_text), + fontSize = 18.sp + ) + } + } + + Spacer(modifier = Modifier.width(1.dp)) + + Button( + onClick = { + navController.showSettings() + }, + modifier = Modifier.width(56.dp), + shape = RoundedCornerShape( + topStart = 0.dp, + bottomStart = 0.dp, + topEnd = 16.dp, + bottomEnd = 16.dp + ), + contentPadding = PaddingValues(0.dp) + ) { + Icon( + imageVector = Icons.Default.Settings, + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + } } CircularProgressIndicator( From 3b7a4576a7108aa75f3cfcf46e0aef4499cfba80 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 18:32:24 +0100 Subject: [PATCH 07/59] wip --- .../java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt | 2 +- .../org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt | 4 ++-- app/src/main/res/values/strings.xml | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt index a4d24b6d..652531d3 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt @@ -247,7 +247,7 @@ fun LoginScreen( .alpha(0.5f) .padding(bottom = 32.dp), text = stringResource(R.string.audiobookshelf_server_is_required), - style = MaterialTheme.typography.bodySmall.copy( + style = typography.bodySmall.copy( fontSize = 10.sp, fontWeight = FontWeight.Normal, color = colorScheme.onBackground, diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt index 5b1efbf5..3152cf27 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt @@ -88,8 +88,8 @@ fun SettingsScreen( ServerComposable(navController, viewModel) GeneralSettingsComposable(viewModel) AdvancedSettingsItemComposable( - title = "Custom Headers", - description = "Define headers for each app request", + title = stringResource(R.string.settings_screen_custom_headers_title), + description = stringResource(R.string.settings_screen_custom_header_hint), onclick = { navController.showCustomHeadersSettings() } ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 862d33ba..fc08e518 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -47,4 +47,6 @@ Library is not available Search by title or author Listening now + Custom Headers + Define headers for each request \ No newline at end of file From 98e3401251f15b1ec6cec0bae131a37193cf36f0 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 18:45:52 +0100 Subject: [PATCH 08/59] wip --- .../lissen/ui/screens/login/LoginScreen.kt | 1 - .../ui/screens/settings/SettingsScreen.kt | 9 +++- .../composable/GeneralSettingsComposable.kt | 44 ++++++++++--------- ...posable.kt => ServerSettingsComposable.kt} | 3 +- 4 files changed, 33 insertions(+), 24 deletions(-) rename app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/{ServerComposable.kt => ServerSettingsComposable.kt} (98%) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt index 652531d3..ede45a75 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/login/LoginScreen.kt @@ -25,7 +25,6 @@ import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt index 3152cf27..e1372bb9 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt @@ -21,6 +21,8 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -31,7 +33,7 @@ import org.grakovne.lissen.ui.navigation.AppNavigationService import org.grakovne.lissen.ui.screens.settings.composable.AdditionalComposable import org.grakovne.lissen.ui.screens.settings.composable.AdvancedSettingsItemComposable import org.grakovne.lissen.ui.screens.settings.composable.GeneralSettingsComposable -import org.grakovne.lissen.ui.screens.settings.composable.ServerComposable +import org.grakovne.lissen.ui.screens.settings.composable.ServerSettingsComposable import org.grakovne.lissen.viewmodel.SettingsViewModel @Composable @@ -41,6 +43,7 @@ fun SettingsScreen( navController: AppNavigationService ) { val viewModel: SettingsViewModel = hiltViewModel() + val host by viewModel.host.observeAsState("") val titleTextStyle = typography.titleLarge.copy(fontWeight = FontWeight.SemiBold) LaunchedEffect(Unit) { @@ -85,7 +88,9 @@ fun SettingsScreen( .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally ) { - ServerComposable(navController, viewModel) + if (host?.isNotEmpty() == true) { + ServerSettingsComposable(navController, viewModel) + } GeneralSettingsComposable(viewModel) AdvancedSettingsItemComposable( title = stringResource(R.string.settings_screen_custom_headers_title), diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/GeneralSettingsComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/GeneralSettingsComposable.kt index c5a104ea..a47b7676 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/GeneralSettingsComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/GeneralSettingsComposable.kt @@ -31,31 +31,35 @@ fun GeneralSettingsComposable(viewModel: SettingsViewModel) { val preferredLibrary by viewModel.preferredLibrary.observeAsState() val preferredColorScheme by viewModel.preferredColorScheme.observeAsState() + val host by viewModel.host.observeAsState("") var preferredLibraryExpanded by remember { mutableStateOf(false) } var colorSchemeExpanded by remember { mutableStateOf(false) } val context = LocalContext.current - Row( - modifier = Modifier - .fillMaxWidth() - .clickable { preferredLibraryExpanded = true } - .padding(horizontal = 16.dp, vertical = 12.dp) - ) { - Column(modifier = Modifier.weight(1f)) { - Text( - text = stringResource(R.string.settings_screen_preferred_library_title), - style = typography.bodyLarge.copy(fontWeight = FontWeight.SemiBold), - modifier = Modifier.padding(bottom = 4.dp) - ) - Text( - text = preferredLibrary?.title ?: stringResource(R.string.library_is_not_available), - style = typography.bodyMedium, - color = when (preferredLibrary?.title) { - null -> colorScheme.onSurfaceVariant.copy(alpha = 0.6f) - else -> colorScheme.onSurfaceVariant - } - ) + if (host?.isNotEmpty() == true) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { preferredLibraryExpanded = true } + .padding(horizontal = 16.dp, vertical = 12.dp) + ) { + Column(modifier = Modifier.weight(1f)) { + Text( + text = stringResource(R.string.settings_screen_preferred_library_title), + style = typography.bodyLarge.copy(fontWeight = FontWeight.SemiBold), + modifier = Modifier.padding(bottom = 4.dp) + ) + Text( + text = preferredLibrary?.title + ?: stringResource(R.string.library_is_not_available), + style = typography.bodyMedium, + color = when (preferredLibrary?.title) { + null -> colorScheme.onSurfaceVariant.copy(alpha = 0.6f) + else -> colorScheme.onSurfaceVariant + } + ) + } } } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/ServerComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/ServerSettingsComposable.kt similarity index 98% rename from app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/ServerComposable.kt rename to app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/ServerSettingsComposable.kt index 0b3c8aa9..17aa10a4 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/ServerComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/ServerSettingsComposable.kt @@ -29,7 +29,7 @@ import org.grakovne.lissen.ui.navigation.AppNavigationService import org.grakovne.lissen.viewmodel.SettingsViewModel @Composable -fun ServerComposable( +fun ServerSettingsComposable( navController: AppNavigationService, viewModel: SettingsViewModel ) { @@ -66,6 +66,7 @@ fun ServerComposable( overflow = TextOverflow.Ellipsis ) } + username?.let { } Text( modifier = Modifier.padding(start = 10.dp, top = 4.dp), text = stringResource( From 9183cf4d5e08760b2856b2bfa4b1edece12431c7 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 18:53:03 +0100 Subject: [PATCH 09/59] wip --- .../advanced/CustomHeadersSettingsScreen.kt | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index eb2cf027..0bd5410b 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -69,33 +69,6 @@ fun CustomHeadersSettingsScreen() { .padding(innerPadding), horizontalAlignment = Alignment.CenterHorizontally ) { - Surface( - modifier = Modifier - .fillMaxWidth() - .padding(top = 12.dp, bottom = 0.dp, start = 16.dp, end = 16.dp), - color = colorScheme.surfaceDim.copy(alpha = 0.1f), - shape = RoundedCornerShape(16.dp) - ) { - Row( - modifier = Modifier.padding(16.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - imageVector = Icons.Outlined.Info, - contentDescription = null, - tint = colorScheme.onBackground - ) - Spacer(modifier = Modifier.width(12.dp)) - Text( - text = "Custom headers will be included in every request", - style = typography.bodyMedium, - color = colorScheme.onBackground, - textAlign = TextAlign.Start, - modifier = Modifier.weight(1f) - ) - } - } - Row( modifier = Modifier .fillMaxWidth() From 7772ef596c76efc5f583a60960ff2381b0d845e1 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 19:25:02 +0100 Subject: [PATCH 10/59] wip --- .../lissen/ui/navigation/AppNavHost.kt | 2 +- .../advanced/CustomHeadersSettingsScreen.kt | 78 +++++++++++++------ 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt index 0e0758bf..bbd96662 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt @@ -35,7 +35,7 @@ fun AppNavHost( ) } val startDestination = when { - hasCredentials -> "login_screen" + hasCredentials -> "settings_screen/custom_headers" else -> "login_screen" } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 0bd5410b..5c96d102 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -1,5 +1,6 @@ package org.grakovne.lissen.ui.screens.settings.advanced +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row @@ -9,17 +10,26 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowBack +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.DeleteOutline import androidx.compose.material.icons.filled.Info +import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.outlined.Info import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField @@ -28,8 +38,13 @@ import androidx.compose.material3.Surface 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.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -38,6 +53,9 @@ import androidx.compose.ui.unit.sp @OptIn(ExperimentalMaterial3Api::class) @Composable fun CustomHeadersSettingsScreen() { + var name by remember { mutableStateOf("") } + var value by remember { mutableStateOf("") } + Scaffold( topBar = { TopAppBar( @@ -70,51 +88,65 @@ fun CustomHeadersSettingsScreen() { horizontalAlignment = Alignment.CenterHorizontally ) { Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - verticalAlignment = Alignment.CenterVertically + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically, ) { Column( - horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.weight(1f) ) { OutlinedTextField( - value = "", - onValueChange = {}, + value = name, + onValueChange = { name = it }, label = { Text("Name") }, - shape = RoundedCornerShape(16.dp), singleLine = true, + shape = RoundedCornerShape(16.dp), modifier = Modifier .fillMaxWidth() + .clickable { } .padding(vertical = 4.dp) ) + OutlinedTextField( - value = "", - onValueChange = {}, + value = value, + onValueChange = { value = it }, label = { Text("Value") }, - shape = RoundedCornerShape(16.dp), singleLine = true, + shape = RoundedCornerShape(16.dp), modifier = Modifier .fillMaxWidth() .padding(vertical = 4.dp) ) + } - Spacer(modifier = Modifier.height(16.dp)) + Spacer(modifier = Modifier.width(16.dp)) - Button( - onClick = {}, - contentPadding = PaddingValues(0.dp) - ) { - Text( - fontSize = 24.sp, - text = " + ", - style = typography.titleLarge, - textAlign = TextAlign.Center - ) - } + IconButton( + onClick = {} + ) { + + Icon( + imageVector = Icons.Default.DeleteOutline, + contentDescription = null, + modifier = Modifier.size(36.dp) + ) } + } + + Spacer(modifier = Modifier.height(16.dp)) + + FloatingActionButton( + containerColor = colorScheme.primary, + shape = CircleShape, + onClick = { }, + ) { + Icon( + + imageVector = Icons.Filled.Add, + contentDescription = "Add" + ) + } + } } ) From 182ecee8ed37b6ff33c34080a7507dd75fbbc4f2 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 20:04:59 +0100 Subject: [PATCH 11/59] wip --- .../advanced/CustomHeaderItemComposable.kt | 84 ++++++++++++++ .../advanced/CustomHeadersSettingsScreen.kt | 109 ++++-------------- 2 files changed, 104 insertions(+), 89 deletions(-) create mode 100644 app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt new file mode 100644 index 00000000..b106ca82 --- /dev/null +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt @@ -0,0 +1,84 @@ +package org.grakovne.lissen.ui.screens.settings.advanced + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.DeleteOutline +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedTextField +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.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun CustomHeaderItemComposable( + headerName: String = "", + headerValue: String = "", + onChanged: (Pair) -> Unit +) { + var name by remember { mutableStateOf(headerName) } + var value by remember { mutableStateOf(headerValue) } + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Column( + modifier = Modifier.weight(1f) + ) { + OutlinedTextField( + value = name, + onValueChange = { name = it }, + label = { Text("Name") }, + singleLine = true, + shape = RoundedCornerShape(16.dp), + modifier = Modifier + .fillMaxWidth() + .clickable { } + .padding(vertical = 4.dp) + ) + + OutlinedTextField( + value = value, + onValueChange = { value = it }, + label = { Text("Value") }, + singleLine = true, + shape = RoundedCornerShape(16.dp), + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp) + ) + } + + Spacer(modifier = Modifier.width(16.dp)) + + IconButton( + onClick = {} + ) { + Icon( + imageVector = Icons.Default.DeleteOutline, + contentDescription = null, + modifier = Modifier.size(36.dp) + ) + } + } + + Spacer(modifier = Modifier.height(16.dp)) +} diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 5c96d102..7e5fca2f 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -1,61 +1,34 @@ package org.grakovne.lissen.ui.screens.settings.advanced -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBarsPadding -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowBack import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Delete -import androidx.compose.material.icons.filled.DeleteOutline -import androidx.compose.material.icons.filled.Info -import androidx.compose.material.icons.filled.Settings -import androidx.compose.material.icons.outlined.Info -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonColors import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FabPosition import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography -import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold -import androidx.compose.material3.Surface 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.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp @OptIn(ExperimentalMaterial3Api::class) @Composable fun CustomHeadersSettingsScreen() { - var name by remember { mutableStateOf("") } - var value by remember { mutableStateOf("") } - Scaffold( topBar = { TopAppBar( @@ -84,69 +57,27 @@ fun CustomHeadersSettingsScreen() { Column( modifier = Modifier .fillMaxSize() + .verticalScroll(rememberScrollState()) .padding(innerPadding), horizontalAlignment = Alignment.CenterHorizontally ) { - Row( - modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - Column( - modifier = Modifier.weight(1f) - ) { - OutlinedTextField( - value = name, - onValueChange = { name = it }, - label = { Text("Name") }, - singleLine = true, - shape = RoundedCornerShape(16.dp), - modifier = Modifier - .fillMaxWidth() - .clickable { } - .padding(vertical = 4.dp) - ) - - OutlinedTextField( - value = value, - onValueChange = { value = it }, - label = { Text("Value") }, - singleLine = true, - shape = RoundedCornerShape(16.dp), - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 4.dp) - ) - } - - Spacer(modifier = Modifier.width(16.dp)) - - IconButton( - onClick = {} - ) { - - Icon( - imageVector = Icons.Default.DeleteOutline, - contentDescription = null, - modifier = Modifier.size(36.dp) - ) - } - - } - - Spacer(modifier = Modifier.height(16.dp)) - - FloatingActionButton( - containerColor = colorScheme.primary, - shape = CircleShape, - onClick = { }, - ) { - Icon( - - imageVector = Icons.Filled.Add, - contentDescription = "Add" - ) - } + CustomHeaderItemComposable() {} + CustomHeaderItemComposable() {} + CustomHeaderItemComposable() {} + } + }, + floatingActionButtonPosition = FabPosition.Center, + floatingActionButton = { + FloatingActionButton( + containerColor = colorScheme.primary, + shape = CircleShape, + onClick = { } + ) { + Icon( + imageVector = Icons.Filled.Add, + contentDescription = "Add" + ) } } ) From f691bad793a0b3a3929c0f4741519bdf352d3dc2 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 20:20:29 +0100 Subject: [PATCH 12/59] wip --- .../ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 7e5fca2f..55244dcd 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -29,6 +29,8 @@ import androidx.compose.ui.text.font.FontWeight @OptIn(ExperimentalMaterial3Api::class) @Composable fun CustomHeadersSettingsScreen() { + val headers = listOf("123" to "456", "789" to "012") + Scaffold( topBar = { TopAppBar( From 6b4fe6ee039e18d7a9228d877e76766a8b676b66 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 20:24:57 +0100 Subject: [PATCH 13/59] wip --- .../advanced/CustomHeaderItemComposable.kt | 8 ++++--- .../advanced/CustomHeadersSettingsScreen.kt | 22 ++++++++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt index b106ca82..5d9d510a 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt @@ -29,7 +29,8 @@ import androidx.compose.ui.unit.dp fun CustomHeaderItemComposable( headerName: String = "", headerValue: String = "", - onChanged: (Pair) -> Unit + onChanged: (Pair) -> Unit, + onDelete: () -> Unit ) { var name by remember { mutableStateOf(headerName) } var value by remember { mutableStateOf(headerValue) } @@ -51,7 +52,7 @@ fun CustomHeaderItemComposable( shape = RoundedCornerShape(16.dp), modifier = Modifier .fillMaxWidth() - .clickable { } + .clickable { onChanged(name to value) } .padding(vertical = 4.dp) ) @@ -63,6 +64,7 @@ fun CustomHeaderItemComposable( shape = RoundedCornerShape(16.dp), modifier = Modifier .fillMaxWidth() + .clickable { onChanged(name to value) } .padding(vertical = 4.dp) ) } @@ -70,7 +72,7 @@ fun CustomHeaderItemComposable( Spacer(modifier = Modifier.width(16.dp)) IconButton( - onClick = {} + onClick = { onDelete() } ) { Icon( imageVector = Icons.Default.DeleteOutline, diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 55244dcd..aba8365c 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -22,6 +22,8 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight @@ -29,7 +31,8 @@ import androidx.compose.ui.text.font.FontWeight @OptIn(ExperimentalMaterial3Api::class) @Composable fun CustomHeadersSettingsScreen() { - val headers = listOf("123" to "456", "789" to "012") + // Make headers a mutableStateListOf + val headers = remember { mutableStateListOf("123" to "456", "789" to "012") } Scaffold( topBar = { @@ -63,9 +66,18 @@ fun CustomHeadersSettingsScreen() { .padding(innerPadding), horizontalAlignment = Alignment.CenterHorizontally ) { - CustomHeaderItemComposable() {} - CustomHeaderItemComposable() {} - CustomHeaderItemComposable() {} + headers.forEachIndexed { index, header -> + CustomHeaderItemComposable( + headerName = header.first, + headerValue = header.second, + onChanged = { newPair -> + headers[index] = newPair + }, + onDelete = { + headers.removeAt(index) + } + ) + } } }, floatingActionButtonPosition = FabPosition.Center, @@ -73,7 +85,7 @@ fun CustomHeadersSettingsScreen() { FloatingActionButton( containerColor = colorScheme.primary, shape = CircleShape, - onClick = { } + onClick = { headers.add("" to "") } ) { Icon( From 700dc7a90efbcc5e2daa997982e9505ecb2b4045 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 20:25:07 +0100 Subject: [PATCH 14/59] wip --- .../ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index aba8365c..97c0ef1b 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -31,7 +31,6 @@ import androidx.compose.ui.text.font.FontWeight @OptIn(ExperimentalMaterial3Api::class) @Composable fun CustomHeadersSettingsScreen() { - // Make headers a mutableStateListOf val headers = remember { mutableStateListOf("123" to "456", "789" to "012") } Scaffold( From e85706b608c3268c7a0a840cf9c33cc8a01a1045 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 21:18:12 +0100 Subject: [PATCH 15/59] wip --- .../advanced/CustomHeaderItemComposable.kt | 25 ++++++----------- .../advanced/CustomHeadersSettingsScreen.kt | 28 +++++++++++++++---- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt index 5d9d510a..97b535f8 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt @@ -1,6 +1,5 @@ package org.grakovne.lissen.ui.screens.settings.advanced -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -18,8 +17,6 @@ import androidx.compose.material3.OutlinedTextField 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.Alignment import androidx.compose.ui.Modifier @@ -27,14 +24,10 @@ import androidx.compose.ui.unit.dp @Composable fun CustomHeaderItemComposable( - headerName: String = "", - headerValue: String = "", - onChanged: (Pair) -> Unit, - onDelete: () -> Unit + header: CustomHeader, + onChanged: (CustomHeader) -> Unit, + onDelete: (CustomHeader) -> Unit ) { - var name by remember { mutableStateOf(headerName) } - var value by remember { mutableStateOf(headerValue) } - Row( modifier = Modifier .fillMaxWidth() @@ -45,26 +38,24 @@ fun CustomHeaderItemComposable( modifier = Modifier.weight(1f) ) { OutlinedTextField( - value = name, - onValueChange = { name = it }, + value = header.name, + onValueChange = { onChanged(header.copy(name = it, value = header.value)) }, label = { Text("Name") }, singleLine = true, shape = RoundedCornerShape(16.dp), modifier = Modifier .fillMaxWidth() - .clickable { onChanged(name to value) } .padding(vertical = 4.dp) ) OutlinedTextField( - value = value, - onValueChange = { value = it }, + value = header.value, + onValueChange = { onChanged(header.copy(name = header.name, value = it)) }, label = { Text("Value") }, singleLine = true, shape = RoundedCornerShape(16.dp), modifier = Modifier .fillMaxWidth() - .clickable { onChanged(name to value) } .padding(vertical = 4.dp) ) } @@ -72,7 +63,7 @@ fun CustomHeaderItemComposable( Spacer(modifier = Modifier.width(16.dp)) IconButton( - onClick = { onDelete() } + onClick = { onDelete(header) } ) { Icon( imageVector = Icons.Default.DeleteOutline, diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 97c0ef1b..588aecab 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -27,11 +27,12 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight +import java.util.UUID @OptIn(ExperimentalMaterial3Api::class) @Composable fun CustomHeadersSettingsScreen() { - val headers = remember { mutableStateListOf("123" to "456", "789" to "012") } + val headers = remember { mutableStateListOf(CustomHeader("123", "234"), CustomHeader("345", "456")) } Scaffold( topBar = { @@ -67,13 +68,16 @@ fun CustomHeadersSettingsScreen() { ) { headers.forEachIndexed { index, header -> CustomHeaderItemComposable( - headerName = header.first, - headerValue = header.second, + header = header, onChanged = { newPair -> headers[index] = newPair }, - onDelete = { - headers.removeAt(index) + onDelete = { pair -> + headers.remove(pair) + + if (headers.isEmpty()) { + headers.add(CustomHeader.empty()) + } } ) } @@ -84,7 +88,7 @@ fun CustomHeadersSettingsScreen() { FloatingActionButton( containerColor = colorScheme.primary, shape = CircleShape, - onClick = { headers.add("" to "") } + onClick = { headers.add(CustomHeader.empty()) } ) { Icon( @@ -95,3 +99,15 @@ fun CustomHeadersSettingsScreen() { } ) } + +data class CustomHeader( + val name: String, + val value: String, + val id: UUID = UUID.randomUUID() +) { + + companion object { + + fun empty() = CustomHeader("", "") + } +} From 84ea9ee7d7d24a99213597a4bf5519a2e6291977 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 21:23:26 +0100 Subject: [PATCH 16/59] wip --- .../lissen/domain/ConnectionCustomHeader.kt | 14 ++++++++++++++ .../advanced/CustomHeaderItemComposable.kt | 3 +-- .../advanced/CustomHeadersSettingsScreen.kt | 13 +------------ 3 files changed, 16 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/org/grakovne/lissen/domain/ConnectionCustomHeader.kt diff --git a/app/src/main/java/org/grakovne/lissen/domain/ConnectionCustomHeader.kt b/app/src/main/java/org/grakovne/lissen/domain/ConnectionCustomHeader.kt new file mode 100644 index 00000000..ccb74ae8 --- /dev/null +++ b/app/src/main/java/org/grakovne/lissen/domain/ConnectionCustomHeader.kt @@ -0,0 +1,14 @@ +package org.grakovne.lissen.domain + +import java.util.UUID + +data class CustomHeader( + val name: String, + val value: String, + val id: UUID = UUID.randomUUID() +) { + + companion object { + fun empty() = CustomHeader("", "") + } +} diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt index 97b535f8..a7c35c00 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt @@ -16,11 +16,10 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import org.grakovne.lissen.domain.CustomHeader @Composable fun CustomHeaderItemComposable( diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 588aecab..11734900 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -27,7 +27,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight -import java.util.UUID +import org.grakovne.lissen.domain.CustomHeader @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -100,14 +100,3 @@ fun CustomHeadersSettingsScreen() { ) } -data class CustomHeader( - val name: String, - val value: String, - val id: UUID = UUID.randomUUID() -) { - - companion object { - - fun empty() = CustomHeader("", "") - } -} From 1ff8547521a995d0451cbabdeeb60ed3ad55f251 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 21:27:31 +0100 Subject: [PATCH 17/59] wip --- .../org/grakovne/lissen/domain/ConnectionCustomHeader.kt | 4 ++-- .../settings/advanced/CustomHeaderItemComposable.kt | 8 ++++---- .../settings/advanced/CustomHeadersSettingsScreen.kt | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/domain/ConnectionCustomHeader.kt b/app/src/main/java/org/grakovne/lissen/domain/ConnectionCustomHeader.kt index ccb74ae8..3f75e9f5 100644 --- a/app/src/main/java/org/grakovne/lissen/domain/ConnectionCustomHeader.kt +++ b/app/src/main/java/org/grakovne/lissen/domain/ConnectionCustomHeader.kt @@ -2,13 +2,13 @@ package org.grakovne.lissen.domain import java.util.UUID -data class CustomHeader( +data class ServerCustomHeader( val name: String, val value: String, val id: UUID = UUID.randomUUID() ) { companion object { - fun empty() = CustomHeader("", "") + fun empty() = ServerCustomHeader("", "") } } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt index a7c35c00..4fe309e2 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt @@ -19,13 +19,13 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import org.grakovne.lissen.domain.CustomHeader +import org.grakovne.lissen.domain.ServerCustomHeader @Composable fun CustomHeaderItemComposable( - header: CustomHeader, - onChanged: (CustomHeader) -> Unit, - onDelete: (CustomHeader) -> Unit + header: ServerCustomHeader, + onChanged: (ServerCustomHeader) -> Unit, + onDelete: (ServerCustomHeader) -> Unit ) { Row( modifier = Modifier diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 11734900..63cc5458 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -27,12 +27,12 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight -import org.grakovne.lissen.domain.CustomHeader +import org.grakovne.lissen.domain.ServerCustomHeader @OptIn(ExperimentalMaterial3Api::class) @Composable fun CustomHeadersSettingsScreen() { - val headers = remember { mutableStateListOf(CustomHeader("123", "234"), CustomHeader("345", "456")) } + val headers = remember { mutableStateListOf(ServerCustomHeader("123", "234"), ServerCustomHeader("345", "456")) } Scaffold( topBar = { @@ -76,7 +76,7 @@ fun CustomHeadersSettingsScreen() { headers.remove(pair) if (headers.isEmpty()) { - headers.add(CustomHeader.empty()) + headers.add(ServerCustomHeader.empty()) } } ) @@ -88,7 +88,7 @@ fun CustomHeadersSettingsScreen() { FloatingActionButton( containerColor = colorScheme.primary, shape = CircleShape, - onClick = { headers.add(CustomHeader.empty()) } + onClick = { headers.add(ServerCustomHeader.empty()) } ) { Icon( From 859b956ed7eb88c67c3fe1f3174b3afa378a480a Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 21:27:52 +0100 Subject: [PATCH 18/59] wip --- .../domain/{ConnectionCustomHeader.kt => ServerCustomHeader.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/org/grakovne/lissen/domain/{ConnectionCustomHeader.kt => ServerCustomHeader.kt} (100%) diff --git a/app/src/main/java/org/grakovne/lissen/domain/ConnectionCustomHeader.kt b/app/src/main/java/org/grakovne/lissen/domain/ServerCustomHeader.kt similarity index 100% rename from app/src/main/java/org/grakovne/lissen/domain/ConnectionCustomHeader.kt rename to app/src/main/java/org/grakovne/lissen/domain/ServerCustomHeader.kt From 2899588598718906d263a53529d7c7567e4c693e Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 21:28:15 +0100 Subject: [PATCH 19/59] wip --- .../lissen/domain/{ => connection}/ServerCustomHeader.kt | 2 +- .../ui/screens/settings/advanced/CustomHeaderItemComposable.kt | 2 +- .../ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename app/src/main/java/org/grakovne/lissen/domain/{ => connection}/ServerCustomHeader.kt (82%) diff --git a/app/src/main/java/org/grakovne/lissen/domain/ServerCustomHeader.kt b/app/src/main/java/org/grakovne/lissen/domain/connection/ServerCustomHeader.kt similarity index 82% rename from app/src/main/java/org/grakovne/lissen/domain/ServerCustomHeader.kt rename to app/src/main/java/org/grakovne/lissen/domain/connection/ServerCustomHeader.kt index 3f75e9f5..4bb2fc56 100644 --- a/app/src/main/java/org/grakovne/lissen/domain/ServerCustomHeader.kt +++ b/app/src/main/java/org/grakovne/lissen/domain/connection/ServerCustomHeader.kt @@ -1,4 +1,4 @@ -package org.grakovne.lissen.domain +package org.grakovne.lissen.domain.connection import java.util.UUID diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt index 4fe309e2..fe97dd13 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt @@ -19,7 +19,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import org.grakovne.lissen.domain.ServerCustomHeader +import org.grakovne.lissen.domain.connection.ServerCustomHeader @Composable fun CustomHeaderItemComposable( diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 63cc5458..3c873b29 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -27,7 +27,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight -import org.grakovne.lissen.domain.ServerCustomHeader +import org.grakovne.lissen.domain.connection.ServerCustomHeader @OptIn(ExperimentalMaterial3Api::class) @Composable From 0a7487dc5130987852179fdc8cd97314ef9947c5 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 21:41:06 +0100 Subject: [PATCH 20/59] wip --- .../preferences/LissenSharedPreferences.kt | 25 +++++++++++++++++++ .../advanced/CustomHeadersSettingsScreen.kt | 1 - .../lissen/viewmodel/SettingsViewModel.kt | 9 +++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt b/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt index bec081f9..ea97813f 100644 --- a/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt +++ b/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt @@ -5,6 +5,8 @@ import android.content.SharedPreferences import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import android.util.Base64 +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -13,6 +15,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import org.grakovne.lissen.channel.common.ChannelCode import org.grakovne.lissen.common.ColorScheme import org.grakovne.lissen.domain.Library +import org.grakovne.lissen.domain.connection.ServerCustomHeader import java.security.KeyStore import java.util.UUID import javax.crypto.Cipher @@ -148,6 +151,24 @@ class LissenSharedPreferences @Inject constructor(@ApplicationContext context: C return decrypt(encrypted) } + fun saveCustomHeaders(headers: List) { + val editor = sharedPreferences.edit() + + val json = gson.toJson(headers) + editor.putString(KEY_CUSTOM_HEADERS, json) + editor.apply() + } + + fun getCustomHeaders(): List { + val json = sharedPreferences.getString(KEY_CUSTOM_HEADERS, null) + val type = object : TypeToken>() {}.type + + return when (json == null) { + true -> listOf() + false -> gson.fromJson(json, type) + } + } + companion object { private const val KEY_ALIAS = "secure_key_alias" @@ -165,6 +186,8 @@ class LissenSharedPreferences @Inject constructor(@ApplicationContext context: C private const val KEY_PREFERRED_COLOR_SCHEME = "preferred_color_scheme" + private const val KEY_CUSTOM_HEADERS = "custom_headers" + private const val ANDROID_KEYSTORE = "AndroidKeyStore" private const val TRANSFORMATION = "AES/GCM/NoPadding" @@ -213,5 +236,7 @@ class LissenSharedPreferences @Inject constructor(@ApplicationContext context: C null } } + + private val gson = Gson() } } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 3c873b29..01bba972 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -99,4 +99,3 @@ fun CustomHeadersSettingsScreen() { } ) } - diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt index aed323bd..20d2fd40 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt @@ -9,6 +9,7 @@ import org.grakovne.lissen.channel.common.ApiResult import org.grakovne.lissen.common.ColorScheme import org.grakovne.lissen.content.LissenMediaProvider import org.grakovne.lissen.domain.Library +import org.grakovne.lissen.domain.connection.ServerCustomHeader import org.grakovne.lissen.persistence.preferences.LissenSharedPreferences import javax.inject.Inject @@ -74,4 +75,12 @@ class SettingsViewModel @Inject constructor( _preferredColorScheme.value = colorScheme preferences.saveColorScheme(colorScheme) } + + fun saveCustomHeaders(headers: List) { + preferences.saveCustomHeaders(headers) + } + + fun fetchCustomHeaders(): List { + return preferences.getCustomHeaders() + } } From 7d4e080fc945f11eba7c6958243b464ae678ea07 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 22:09:39 +0100 Subject: [PATCH 21/59] wip --- .../preferences/LissenSharedPreferences.kt | 4 +- .../advanced/CustomHeadersSettingsScreen.kt | 47 +++++++++++++------ .../lissen/viewmodel/SettingsViewModel.kt | 12 +++-- 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt b/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt index ea97813f..c84231b8 100644 --- a/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt +++ b/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt @@ -159,12 +159,12 @@ class LissenSharedPreferences @Inject constructor(@ApplicationContext context: C editor.apply() } - fun getCustomHeaders(): List { + fun getCustomHeaders(): List? { val json = sharedPreferences.getString(KEY_CUSTOM_HEADERS, null) val type = object : TypeToken>() {}.type return when (json == null) { - true -> listOf() + true -> null false -> gson.fromJson(json, type) } } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 01bba972..884e4a32 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -22,17 +22,21 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight +import androidx.hilt.navigation.compose.hiltViewModel import org.grakovne.lissen.domain.connection.ServerCustomHeader +import org.grakovne.lissen.viewmodel.SettingsViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable fun CustomHeadersSettingsScreen() { - val headers = remember { mutableStateListOf(ServerCustomHeader("123", "234"), ServerCustomHeader("345", "456")) } + val settingsViewModel: SettingsViewModel = hiltViewModel() + val headers = settingsViewModel.customHeaders.observeAsState(emptyList()) Scaffold( topBar = { @@ -66,21 +70,29 @@ fun CustomHeadersSettingsScreen() { .padding(innerPadding), horizontalAlignment = Alignment.CenterHorizontally ) { - headers.forEachIndexed { index, header -> - CustomHeaderItemComposable( - header = header, - onChanged = { newPair -> - headers[index] = newPair - }, - onDelete = { pair -> - headers.remove(pair) + headers + .value + .forEachIndexed { index, header -> + CustomHeaderItemComposable( + header = header, + onChanged = { newPair -> + val updatedList = headers.value.toMutableList() + updatedList[index] = newPair - if (headers.isEmpty()) { - headers.add(ServerCustomHeader.empty()) + settingsViewModel.updateCustomHeaders(updatedList) + }, + onDelete = { pair -> + val updatedList = headers.value.toMutableList() + updatedList.remove(pair) + + if (updatedList.isEmpty()) { + updatedList.add(ServerCustomHeader.empty()) + } + + settingsViewModel.updateCustomHeaders(updatedList) } - } - ) - } + ) + } } }, floatingActionButtonPosition = FabPosition.Center, @@ -88,7 +100,12 @@ fun CustomHeadersSettingsScreen() { FloatingActionButton( containerColor = colorScheme.primary, shape = CircleShape, - onClick = { headers.add(ServerCustomHeader.empty()) } + onClick = { + val updatedList = headers.value.toMutableList() + updatedList.add(ServerCustomHeader.empty()) + + settingsViewModel.updateCustomHeaders(updatedList) + } ) { Icon( diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt index 20d2fd40..c0b193a0 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt @@ -34,6 +34,9 @@ class SettingsViewModel @Inject constructor( private val _preferredColorScheme = MutableLiveData(preferences.getColorScheme()) val preferredColorScheme = _preferredColorScheme + private val _customHeaders = MutableLiveData(fetchCustomHeaders()) + val customHeaders = _customHeaders + fun logout() { preferences.clearPreferences() @@ -76,11 +79,12 @@ class SettingsViewModel @Inject constructor( preferences.saveColorScheme(colorScheme) } - fun saveCustomHeaders(headers: List) { + fun updateCustomHeaders(headers: List) { + _customHeaders.value = headers preferences.saveCustomHeaders(headers) } - fun fetchCustomHeaders(): List { - return preferences.getCustomHeaders() - } + private fun fetchCustomHeaders(): List = preferences + .getCustomHeaders() + ?: listOf(ServerCustomHeader.empty()) } From 4e2db2e7408b43c57909ad419b3f2238264e9fc2 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 22:42:40 +0100 Subject: [PATCH 22/59] wip --- .../org/grakovne/lissen/LissenApplication.kt | 56 +++++++++---------- .../api/AudioBookshelfDataRepository.kt | 49 ++++++++++++---- .../api/AudioBookshelfMediaRepository.kt | 29 +++++++++- .../lissen/channel/common/ApiClient.kt | 9 +++ .../lissen/channel/common/BinaryApiClient.kt | 12 +++- .../domain/connection/ServerCustomHeader.kt | 8 +++ .../advanced/CustomHeadersSettingsScreen.kt | 2 - 7 files changed, 115 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/LissenApplication.kt b/app/src/main/java/org/grakovne/lissen/LissenApplication.kt index 88270553..d6c64825 100644 --- a/app/src/main/java/org/grakovne/lissen/LissenApplication.kt +++ b/app/src/main/java/org/grakovne/lissen/LissenApplication.kt @@ -3,12 +3,6 @@ package org.grakovne.lissen import android.app.Application import android.content.Context import dagger.hilt.android.HiltAndroidApp -import org.acra.ReportField -import org.acra.config.httpSender -import org.acra.config.toast -import org.acra.data.StringFormat -import org.acra.ktx.initAcra -import org.acra.sender.HttpSender @HiltAndroidApp class LissenApplication : Application() { @@ -20,31 +14,31 @@ class LissenApplication : Application() { override fun attachBaseContext(base: Context) { super.attachBaseContext(base) - initAcra { - buildConfigClass = BuildConfig::class.java - reportFormat = StringFormat.JSON - - httpSender { - uri = "https://acrarium.grakovne.org/report" - basicAuthLogin = "6KmG6qnp09eDT4Wo" - basicAuthPassword = "ZWBOP83LM0SjrOFC" - httpMethod = HttpSender.Method.POST - dropReportsOnTimeout = false - } - - toast { - text = getString(R.string.app_crach_toast) - } - - reportContent = listOf( - ReportField.APP_VERSION_NAME, - ReportField.APP_VERSION_CODE, - ReportField.ANDROID_VERSION, - ReportField.PHONE_MODEL, - ReportField.STACK_TRACE, - ReportField.LOGCAT - ) - } +// initAcra { +// buildConfigClass = BuildConfig::class.java +// reportFormat = StringFormat.JSON +// +// httpSender { +// uri = "https://acrarium.grakovne.org/report" +// basicAuthLogin = "6KmG6qnp09eDT4Wo" +// basicAuthPassword = "ZWBOP83LM0SjrOFC" +// httpMethod = HttpSender.Method.POST +// dropReportsOnTimeout = false +// } +// +// toast { +// text = getString(R.string.app_crach_toast) +// } +// +// reportContent = listOf( +// ReportField.APP_VERSION_NAME, +// ReportField.APP_VERSION_CODE, +// ReportField.ANDROID_VERSION, +// ReportField.PHONE_MODEL, +// ReportField.STACK_TRACE, +// ReportField.LOGCAT +// ) +// } } companion object { diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt index 8952912f..2539658a 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt @@ -33,6 +33,8 @@ class AudioBookshelfDataRepository @Inject constructor( @Volatile private var secureClient: AudiobookshelfApiClient? = null + private var cachedCustomHeaders = preferences.getCustomHeaders() + suspend fun fetchLibraries(): ApiResult = safeApiCall { getClientInstance().fetchLibraries() } @@ -110,7 +112,7 @@ class AudioBookshelfDataRepository @Inject constructor( lateinit var apiService: AudiobookshelfApiClient try { - val apiClient = ApiClient(host = host) + val apiClient = ApiClient(host = host, customHeaders = preferences.getCustomHeaders()) apiService = apiClient.retrofit.create(AudiobookshelfApiClient::class.java) } catch (e: Exception) { return ApiResult.Error(ApiError.InternalError) @@ -118,14 +120,16 @@ class AudioBookshelfDataRepository @Inject constructor( val response: ApiResult = safeApiCall { apiService.login(LoginRequest(username, password)) } - return response.fold( - onSuccess = { - loginResponseConverter - .apply(it) - .let { ApiResult.Success(it) } - }, - onFailure = { ApiResult.Error(it.code) } - ) + + return response + .fold( + onSuccess = { + loginResponseConverter + .apply(it) + .let { ApiResult.Success(it) } + }, + onFailure = { ApiResult.Error(it.code) } + ) } private suspend fun safeApiCall( @@ -162,12 +166,33 @@ class AudioBookshelfDataRepository @Inject constructor( throw IllegalStateException("Host or token is missing") } - return secureClient ?: run { - val apiClient = ApiClient(host = host, token = token) - apiClient.retrofit.create(AudiobookshelfApiClient::class.java) + return when (cachedCustomHeaders == preferences.getCustomHeaders()) { + true -> { + secureClient ?: run { + val apiClient = apiClient(host, token) + apiClient.retrofit.create(AudiobookshelfApiClient::class.java) + } + } + + false -> { + apiClient(host, token) + .retrofit + .create(AudiobookshelfApiClient::class.java) + .also { cachedCustomHeaders = preferences.getCustomHeaders() } + .also { secureClient = it } + } } } + private fun apiClient( + host: String, + token: String? + ): ApiClient = ApiClient( + host = host, + token = token, + customHeaders = preferences.getCustomHeaders() + ) + companion object { val urlPattern = Regex("^(http|https)://.*\$") diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt index bbda1b2a..3cf109be 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt @@ -20,6 +20,8 @@ class AudioBookshelfMediaRepository @Inject constructor( @Volatile private var secureClient: AudiobookshelfMediaClient? = null + private var cachedCustomHeaders = preferences.getCustomHeaders() + suspend fun fetchBookCover(itemId: String): ApiResult = safeApiCall { getClientInstance().getItemCover(itemId) } @@ -58,9 +60,30 @@ class AudioBookshelfMediaRepository @Inject constructor( throw IllegalStateException("Host or token is missing") } - return secureClient ?: run { - val apiClient = BinaryApiClient(host, token) - apiClient.retrofit.create(AudiobookshelfMediaClient::class.java) + return when (cachedCustomHeaders == preferences.getCustomHeaders()) { + true -> { + secureClient ?: run { + val apiClient = apiClient(host, token) + apiClient.retrofit.create(AudiobookshelfMediaClient::class.java) + } + } + + false -> { + apiClient(host, token) + .retrofit + .create(AudiobookshelfMediaClient::class.java) + .also { cachedCustomHeaders = preferences.getCustomHeaders() } + .also { secureClient = it } + } } } + + private fun apiClient( + host: String, + token: String + ): BinaryApiClient = BinaryApiClient( + host = host, + token = token, + customHeaders = preferences.getCustomHeaders() + ) } diff --git a/app/src/main/java/org/grakovne/lissen/channel/common/ApiClient.kt b/app/src/main/java/org/grakovne/lissen/channel/common/ApiClient.kt index 4094a9e7..358a68c5 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/common/ApiClient.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/common/ApiClient.kt @@ -4,12 +4,15 @@ import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.logging.HttpLoggingInterceptor +import org.grakovne.lissen.domain.connection.ServerCustomHeader +import org.grakovne.lissen.domain.connection.ServerCustomHeader.Companion.clean import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit class ApiClient( host: String, + customHeaders: List?, token: String? = null ) { @@ -28,6 +31,12 @@ class ApiClient( } requestBuilder.header("User-Agent", USER_AGENT) + + customHeaders + ?.filter { it.name.isNotEmpty() } + ?.filter { it.value.isNotEmpty() } + ?.forEach { requestBuilder.header(it.name.clean(), it.value.clean()) } + val request: Request = requestBuilder.build() chain.proceed(request) } diff --git a/app/src/main/java/org/grakovne/lissen/channel/common/BinaryApiClient.kt b/app/src/main/java/org/grakovne/lissen/channel/common/BinaryApiClient.kt index a032fd75..339a913e 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/common/BinaryApiClient.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/common/BinaryApiClient.kt @@ -3,11 +3,14 @@ package org.grakovne.lissen.channel.common import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor +import org.grakovne.lissen.domain.connection.ServerCustomHeader +import org.grakovne.lissen.domain.connection.ServerCustomHeader.Companion.clean import retrofit2.Retrofit import java.util.concurrent.TimeUnit class BinaryApiClient( host: String, + customHeaders: List?, token: String ) { @@ -23,8 +26,13 @@ class BinaryApiClient( .newBuilder() .header("Authorization", "Bearer $token") .header("User-Agent", USER_AGENT) - .build() - chain.proceed(request) + + customHeaders + ?.filter { it.name.isNotEmpty() } + ?.filter { it.value.isNotEmpty() } + ?.forEach { request.header(it.name.clean(), it.value.clean()) } + + chain.proceed(request.build()) } .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) diff --git a/app/src/main/java/org/grakovne/lissen/domain/connection/ServerCustomHeader.kt b/app/src/main/java/org/grakovne/lissen/domain/connection/ServerCustomHeader.kt index 4bb2fc56..9974401e 100644 --- a/app/src/main/java/org/grakovne/lissen/domain/connection/ServerCustomHeader.kt +++ b/app/src/main/java/org/grakovne/lissen/domain/connection/ServerCustomHeader.kt @@ -10,5 +10,13 @@ data class ServerCustomHeader( companion object { fun empty() = ServerCustomHeader("", "") + + fun String.clean(): String { + var sanitized = this.replace(Regex("[\\r\\n]"), "") + sanitized = sanitized.replace(Regex("[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]"), "") + sanitized = sanitized.trim() + + return sanitized + } } } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 884e4a32..e69071a3 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -23,8 +23,6 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight From 83ae350dc896601ec2f3a033a02a8f37d15c7598 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 22:52:45 +0100 Subject: [PATCH 23/59] wip --- .../api/AudioBookshelfDataRepository.kt | 26 +++---------------- .../api/AudioBookshelfMediaRepository.kt | 24 +++-------------- 2 files changed, 6 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt index 2539658a..4a31f96f 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt @@ -30,11 +30,6 @@ class AudioBookshelfDataRepository @Inject constructor( private val preferences: LissenSharedPreferences ) { - @Volatile - private var secureClient: AudiobookshelfApiClient? = null - - private var cachedCustomHeaders = preferences.getCustomHeaders() - suspend fun fetchLibraries(): ApiResult = safeApiCall { getClientInstance().fetchLibraries() } @@ -103,8 +98,6 @@ class AudioBookshelfDataRepository @Inject constructor( username: String, password: String ): ApiResult { - secureClient = null - if (host.isBlank() || !urlPattern.matches(host)) { return ApiResult.Error(ApiError.InvalidCredentialsHost) } @@ -166,22 +159,9 @@ class AudioBookshelfDataRepository @Inject constructor( throw IllegalStateException("Host or token is missing") } - return when (cachedCustomHeaders == preferences.getCustomHeaders()) { - true -> { - secureClient ?: run { - val apiClient = apiClient(host, token) - apiClient.retrofit.create(AudiobookshelfApiClient::class.java) - } - } - - false -> { - apiClient(host, token) - .retrofit - .create(AudiobookshelfApiClient::class.java) - .also { cachedCustomHeaders = preferences.getCustomHeaders() } - .also { secureClient = it } - } - } + return apiClient(host, token) + .retrofit + .create(AudiobookshelfApiClient::class.java) } private fun apiClient( diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt index 3cf109be..e470627c 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt @@ -17,11 +17,6 @@ class AudioBookshelfMediaRepository @Inject constructor( private val preferences: LissenSharedPreferences ) { - @Volatile - private var secureClient: AudiobookshelfMediaClient? = null - - private var cachedCustomHeaders = preferences.getCustomHeaders() - suspend fun fetchBookCover(itemId: String): ApiResult = safeApiCall { getClientInstance().getItemCover(itemId) } @@ -60,22 +55,9 @@ class AudioBookshelfMediaRepository @Inject constructor( throw IllegalStateException("Host or token is missing") } - return when (cachedCustomHeaders == preferences.getCustomHeaders()) { - true -> { - secureClient ?: run { - val apiClient = apiClient(host, token) - apiClient.retrofit.create(AudiobookshelfMediaClient::class.java) - } - } - - false -> { - apiClient(host, token) - .retrofit - .create(AudiobookshelfMediaClient::class.java) - .also { cachedCustomHeaders = preferences.getCustomHeaders() } - .also { secureClient = it } - } - } + return apiClient(host, token) + .retrofit + .create(AudiobookshelfMediaClient::class.java) } private fun apiClient( From 3a590cab660f9a05cbc26c6033aa0add76adeff2 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 23:02:57 +0100 Subject: [PATCH 24/59] wip --- .../java/org/grakovne/lissen/ui/navigation/AppNavHost.kt | 4 +++- .../settings/advanced/CustomHeadersSettingsScreen.kt | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt index bbd96662..347e956d 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt @@ -84,7 +84,9 @@ fun AppNavHost( } composable("settings_screen/custom_headers") { - CustomHeadersSettingsScreen() + CustomHeadersSettingsScreen( + onBack = { navController.popBackStack() } + ) } } } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index e69071a3..f8ae2012 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -32,7 +32,9 @@ import org.grakovne.lissen.viewmodel.SettingsViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable -fun CustomHeadersSettingsScreen() { +fun CustomHeadersSettingsScreen( + onBack: () -> Unit +) { val settingsViewModel: SettingsViewModel = hiltViewModel() val headers = settingsViewModel.customHeaders.observeAsState(emptyList()) @@ -47,7 +49,7 @@ fun CustomHeadersSettingsScreen() { ) }, navigationIcon = { - IconButton(onClick = { }) { + IconButton(onClick = { onBack() }) { Icon( imageVector = Icons.AutoMirrored.Outlined.ArrowBack, contentDescription = "Back", From 5eda8c79234894436738f1985f9bca1ce28b1453 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 23:07:32 +0100 Subject: [PATCH 25/59] wip --- .../main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt index 347e956d..a4eed3a5 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/navigation/AppNavHost.kt @@ -35,7 +35,7 @@ fun AppNavHost( ) } val startDestination = when { - hasCredentials -> "settings_screen/custom_headers" + hasCredentials -> "library_screen" else -> "login_screen" } From 032ecd2f69f4333be7ecfbe797df42068030d4b9 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 23:21:36 +0100 Subject: [PATCH 26/59] wip --- .../audiobookshelf/api/ApiClientConfig.kt | 9 ++++++++ .../api/AudioBookshelfDataRepository.kt | 23 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/ApiClientConfig.kt diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/ApiClientConfig.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/ApiClientConfig.kt new file mode 100644 index 00000000..add23ba4 --- /dev/null +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/ApiClientConfig.kt @@ -0,0 +1,9 @@ +package org.grakovne.lissen.channel.audiobookshelf.api + +import org.grakovne.lissen.domain.connection.ServerCustomHeader + +data class ApiClientConfig ( + val host: String?, + val token: String?, + val customHeaders: List? +) \ No newline at end of file diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt index 4a31f96f..5a459b94 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt @@ -30,6 +30,9 @@ class AudioBookshelfDataRepository @Inject constructor( private val preferences: LissenSharedPreferences ) { + private var configCache: ApiClientConfig? = null + private var clientCache: AudiobookshelfApiClient = createClientInstance() + suspend fun fetchLibraries(): ApiResult = safeApiCall { getClientInstance().fetchLibraries() } @@ -155,6 +158,26 @@ class AudioBookshelfDataRepository @Inject constructor( val host = preferences.getHost() val token = preferences.getToken() + val cache = ApiClientConfig( + host = host, + token = token, + customHeaders = preferences.getCustomHeaders() + ) + + if (cache != configCache) { + val instance = createClientInstance() + configCache = cache + clientCache = instance + return instance + } + + return clientCache + } + + private fun createClientInstance(): AudiobookshelfApiClient { + val host = preferences.getHost() + val token = preferences.getToken() + if (host.isNullOrBlank() || token.isNullOrBlank()) { throw IllegalStateException("Host or token is missing") } From 48aa54c06190b274dcf2fc29aae48e7f202b67e3 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 23:32:26 +0100 Subject: [PATCH 27/59] wip --- .../api/AudioBookshelfDataRepository.kt | 21 ++++++++++++------- .../advanced/CustomHeadersSettingsScreen.kt | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt index 5a459b94..0e98164f 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt @@ -31,7 +31,7 @@ class AudioBookshelfDataRepository @Inject constructor( ) { private var configCache: ApiClientConfig? = null - private var clientCache: AudiobookshelfApiClient = createClientInstance() + private var clientCache: AudiobookshelfApiClient? = null suspend fun fetchLibraries(): ApiResult = safeApiCall { getClientInstance().fetchLibraries() } @@ -164,14 +164,19 @@ class AudioBookshelfDataRepository @Inject constructor( customHeaders = preferences.getCustomHeaders() ) - if (cache != configCache) { - val instance = createClientInstance() - configCache = cache - clientCache = instance - return instance - } + val currentClientCache = clientCache + + return when (currentClientCache == null || cache != configCache) { + true -> { + val instance = createClientInstance() + configCache = cache + clientCache = instance + instance + } - return clientCache + else -> currentClientCache + + } } private fun createClientInstance(): AudiobookshelfApiClient { diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index f8ae2012..6a10dd5b 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -49,7 +49,7 @@ fun CustomHeadersSettingsScreen( ) }, navigationIcon = { - IconButton(onClick = { onBack() }) { + IconButton(onClick = onBack) { Icon( imageVector = Icons.AutoMirrored.Outlined.ArrowBack, contentDescription = "Back", From 33c75b19ce296b83590f29ce576711c0bb4f9708 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 23:34:54 +0100 Subject: [PATCH 28/59] wip --- .../audiobookshelf/api/ApiClientConfig.kt | 4 +-- .../api/AudioBookshelfDataRepository.kt | 1 - .../api/AudioBookshelfMediaRepository.kt | 27 +++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/ApiClientConfig.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/ApiClientConfig.kt index add23ba4..c1f3baf8 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/ApiClientConfig.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/ApiClientConfig.kt @@ -2,8 +2,8 @@ package org.grakovne.lissen.channel.audiobookshelf.api import org.grakovne.lissen.domain.connection.ServerCustomHeader -data class ApiClientConfig ( +data class ApiClientConfig( val host: String?, val token: String?, val customHeaders: List? -) \ No newline at end of file +) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt index 0e98164f..0d83ed95 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt @@ -175,7 +175,6 @@ class AudioBookshelfDataRepository @Inject constructor( } else -> currentClientCache - } } diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt index e470627c..79089e7c 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt @@ -17,6 +17,9 @@ class AudioBookshelfMediaRepository @Inject constructor( private val preferences: LissenSharedPreferences ) { + private var configCache: ApiClientConfig? = null + private var clientCache: AudiobookshelfMediaClient? = null + suspend fun fetchBookCover(itemId: String): ApiResult = safeApiCall { getClientInstance().getItemCover(itemId) } @@ -51,6 +54,30 @@ class AudioBookshelfMediaRepository @Inject constructor( val host = preferences.getHost() val token = preferences.getToken() + val cache = ApiClientConfig( + host = host, + token = token, + customHeaders = preferences.getCustomHeaders() + ) + + val currentClientCache = clientCache + + return when (currentClientCache == null || cache != configCache) { + true -> { + val instance = createClientInstance() + configCache = cache + clientCache = instance + instance + } + + else -> currentClientCache + } + } + + private fun createClientInstance(): AudiobookshelfMediaClient { + val host = preferences.getHost() + val token = preferences.getToken() + if (host.isNullOrBlank() || token.isNullOrBlank()) { throw IllegalStateException("Host or token is missing") } From 45901ed37c57d1a7a702e870431e5215c1cfd9c8 Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 23:36:00 +0100 Subject: [PATCH 29/59] wip --- .../channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt index 79089e7c..b6468f8b 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt @@ -45,7 +45,6 @@ class AudioBookshelfMediaRepository @Inject constructor( } catch (e: IOException) { ApiResult.Error(ApiError.NetworkError) } catch (e: Exception) { - println(e) ApiResult.Error(ApiError.InternalError) } } From de3b8499cc44b965a083d79a3bd490d89eafdc3d Mon Sep 17 00:00:00 2001 From: grakovne Date: Tue, 5 Nov 2024 23:36:24 +0100 Subject: [PATCH 30/59] wip --- .../org/grakovne/lissen/LissenApplication.kt | 56 ++++++++++--------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/LissenApplication.kt b/app/src/main/java/org/grakovne/lissen/LissenApplication.kt index d6c64825..88270553 100644 --- a/app/src/main/java/org/grakovne/lissen/LissenApplication.kt +++ b/app/src/main/java/org/grakovne/lissen/LissenApplication.kt @@ -3,6 +3,12 @@ package org.grakovne.lissen import android.app.Application import android.content.Context import dagger.hilt.android.HiltAndroidApp +import org.acra.ReportField +import org.acra.config.httpSender +import org.acra.config.toast +import org.acra.data.StringFormat +import org.acra.ktx.initAcra +import org.acra.sender.HttpSender @HiltAndroidApp class LissenApplication : Application() { @@ -14,31 +20,31 @@ class LissenApplication : Application() { override fun attachBaseContext(base: Context) { super.attachBaseContext(base) -// initAcra { -// buildConfigClass = BuildConfig::class.java -// reportFormat = StringFormat.JSON -// -// httpSender { -// uri = "https://acrarium.grakovne.org/report" -// basicAuthLogin = "6KmG6qnp09eDT4Wo" -// basicAuthPassword = "ZWBOP83LM0SjrOFC" -// httpMethod = HttpSender.Method.POST -// dropReportsOnTimeout = false -// } -// -// toast { -// text = getString(R.string.app_crach_toast) -// } -// -// reportContent = listOf( -// ReportField.APP_VERSION_NAME, -// ReportField.APP_VERSION_CODE, -// ReportField.ANDROID_VERSION, -// ReportField.PHONE_MODEL, -// ReportField.STACK_TRACE, -// ReportField.LOGCAT -// ) -// } + initAcra { + buildConfigClass = BuildConfig::class.java + reportFormat = StringFormat.JSON + + httpSender { + uri = "https://acrarium.grakovne.org/report" + basicAuthLogin = "6KmG6qnp09eDT4Wo" + basicAuthPassword = "ZWBOP83LM0SjrOFC" + httpMethod = HttpSender.Method.POST + dropReportsOnTimeout = false + } + + toast { + text = getString(R.string.app_crach_toast) + } + + reportContent = listOf( + ReportField.APP_VERSION_NAME, + ReportField.APP_VERSION_CODE, + ReportField.ANDROID_VERSION, + ReportField.PHONE_MODEL, + ReportField.STACK_TRACE, + ReportField.LOGCAT + ) + } } companion object { From 254530227d77bf8bdbcaf3bb31845f539f0bedf4 Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 00:42:30 +0100 Subject: [PATCH 31/59] wip --- app/build.gradle.kts | 4 ++-- .../grakovne/lissen/ui/screens/settings/SettingsScreen.kt | 3 +-- ...mHeaderItemComposable.kt => CustomHeaderComposable.kt} | 8 +++++--- .../settings/advanced/CustomHeadersSettingsScreen.kt | 6 ++++-- app/src/main/res/values-ru/strings.xml | 5 +++++ app/src/main/res/values/strings.xml | 5 ++++- 6 files changed, 21 insertions(+), 10 deletions(-) rename app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/{CustomHeaderItemComposable.kt => CustomHeaderComposable.kt} (90%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index dca67307..6aaa3520 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -25,8 +25,8 @@ android { applicationId = "org.grakovne.lissen" minSdk = 28 targetSdk = 35 - versionCode = 22 - versionName = "1.0.21" + versionCode = 23 + versionName = "1.0.22-dev" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt index e1372bb9..387d29d1 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/SettingsScreen.kt @@ -44,7 +44,6 @@ fun SettingsScreen( ) { val viewModel: SettingsViewModel = hiltViewModel() val host by viewModel.host.observeAsState("") - val titleTextStyle = typography.titleLarge.copy(fontWeight = FontWeight.SemiBold) LaunchedEffect(Unit) { viewModel.fetchLibraries() @@ -56,7 +55,7 @@ fun SettingsScreen( title = { Text( text = stringResource(R.string.settings_screen_title), - style = titleTextStyle, + style = typography.titleLarge.copy(fontWeight = FontWeight.SemiBold), color = colorScheme.onSurface ) }, diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt similarity index 90% rename from app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt rename to app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt index fe97dd13..929369f5 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderItemComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt @@ -18,11 +18,13 @@ 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.unit.dp +import org.grakovne.lissen.R import org.grakovne.lissen.domain.connection.ServerCustomHeader @Composable -fun CustomHeaderItemComposable( +fun CustomHeaderComposable( header: ServerCustomHeader, onChanged: (ServerCustomHeader) -> Unit, onDelete: (ServerCustomHeader) -> Unit @@ -39,7 +41,7 @@ fun CustomHeaderItemComposable( OutlinedTextField( value = header.name, onValueChange = { onChanged(header.copy(name = it, value = header.value)) }, - label = { Text("Name") }, + label = { Text(stringResource(R.string.custom_header_hint_name)) }, singleLine = true, shape = RoundedCornerShape(16.dp), modifier = Modifier @@ -50,7 +52,7 @@ fun CustomHeaderItemComposable( OutlinedTextField( value = header.value, onValueChange = { onChanged(header.copy(name = header.name, value = it)) }, - label = { Text("Value") }, + label = { Text(stringResource(R.string.custom_header_hint_value)) }, singleLine = true, shape = RoundedCornerShape(16.dp), modifier = Modifier diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 6a10dd5b..0e9f2ffc 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -25,8 +25,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.hilt.navigation.compose.hiltViewModel +import org.grakovne.lissen.R import org.grakovne.lissen.domain.connection.ServerCustomHeader import org.grakovne.lissen.viewmodel.SettingsViewModel @@ -43,7 +45,7 @@ fun CustomHeadersSettingsScreen( TopAppBar( title = { Text( - text = "Custom Headers", + text = stringResource(R.string.custom_headers_title), style = typography.titleLarge.copy(fontWeight = FontWeight.SemiBold), color = colorScheme.onSurface ) @@ -73,7 +75,7 @@ fun CustomHeadersSettingsScreen( headers .value .forEachIndexed { index, header -> - CustomHeaderItemComposable( + CustomHeaderComposable( header = header, onChanged = { newPair -> val updatedList = headers.value.toMutableList() diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 9a2ab83e..854bd263 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -47,4 +47,9 @@ Список библиотек недоступен Поиск по автору или названию Сейчас играет + Прокси-заголовки + Заголовки для подключения к серверу + Ключ + Значение + Прокси-заголовки \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fc08e518..a43d942f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,5 +48,8 @@ Search by title or author Listening now Custom Headers - Define headers for each request + Specify headers for server connections + Key + Value + Custom Headers \ No newline at end of file From 31c66486c6fef62fd398903dbc4a26677d72ea15 Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 00:43:31 +0100 Subject: [PATCH 32/59] wip --- .../settings/composable/AdvancedSettingsItemComposable.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/AdvancedSettingsItemComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/AdvancedSettingsItemComposable.kt index 1f6ee4a9..4d80fbc1 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/AdvancedSettingsItemComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/AdvancedSettingsItemComposable.kt @@ -39,7 +39,7 @@ fun AdvancedSettingsItemComposable( Text( text = description, style = typography.bodyMedium, - color = colorScheme.onSurfaceVariant.copy(alpha = 0.6f) + color = colorScheme.onSurfaceVariant.copy(alpha = 0.8f) ) } Icon( From 4176b917ce438ce6390b8c0b25db94479374e65e Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 00:44:28 +0100 Subject: [PATCH 33/59] wip --- .../composable/AdvancedSettingsItemComposable.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/AdvancedSettingsItemComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/AdvancedSettingsItemComposable.kt index 4d80fbc1..d82547cf 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/AdvancedSettingsItemComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/composable/AdvancedSettingsItemComposable.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @Composable @@ -34,12 +35,16 @@ fun AdvancedSettingsItemComposable( Text( text = title, style = typography.bodyLarge.copy(fontWeight = FontWeight.SemiBold), - modifier = Modifier.padding(bottom = 4.dp) + modifier = Modifier.padding(bottom = 4.dp), + maxLines = 1, + overflow = TextOverflow.Ellipsis ) Text( text = description, style = typography.bodyMedium, - color = colorScheme.onSurfaceVariant.copy(alpha = 0.8f) + color = colorScheme.onSurfaceVariant.copy(alpha = 0.8f), + maxLines = 1, + overflow = TextOverflow.Ellipsis ) } Icon( From c446d5bbcf46fae3f4af7f6b9b93eb8b990c9562 Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 00:50:09 +0100 Subject: [PATCH 34/59] wip --- .../advanced/CustomHeadersSettingsScreen.kt | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 0e9f2ffc..278e4572 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -1,13 +1,12 @@ package org.grakovne.lissen.ui.screens.settings.advanced -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsPadding -import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowBack import androidx.compose.material.icons.filled.Add @@ -27,6 +26,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import org.grakovne.lissen.R import org.grakovne.lissen.domain.connection.ServerCustomHeader @@ -40,6 +40,9 @@ fun CustomHeadersSettingsScreen( val settingsViewModel: SettingsViewModel = hiltViewModel() val headers = settingsViewModel.customHeaders.observeAsState(emptyList()) + val fabHeight = 56.dp + val additionalPadding = 16.dp + Scaffold( topBar = { TopAppBar( @@ -65,36 +68,35 @@ fun CustomHeadersSettingsScreen( .systemBarsPadding() .fillMaxHeight(), content = { innerPadding -> - Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding(innerPadding), + LazyColumn( + contentPadding = PaddingValues( + top = innerPadding.calculateTopPadding(), + bottom = innerPadding.calculateBottomPadding() + fabHeight + additionalPadding + ), + modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally ) { - headers - .value - .forEachIndexed { index, header -> - CustomHeaderComposable( - header = header, - onChanged = { newPair -> - val updatedList = headers.value.toMutableList() - updatedList[index] = newPair - - settingsViewModel.updateCustomHeaders(updatedList) - }, - onDelete = { pair -> - val updatedList = headers.value.toMutableList() - updatedList.remove(pair) + itemsIndexed(headers.value) { index, header -> + CustomHeaderComposable( + header = header, + onChanged = { newPair -> + val updatedList = headers.value.toMutableList() + updatedList[index] = newPair - if (updatedList.isEmpty()) { - updatedList.add(ServerCustomHeader.empty()) - } + settingsViewModel.updateCustomHeaders(updatedList) + }, + onDelete = { pair -> + val updatedList = headers.value.toMutableList() + updatedList.remove(pair) - settingsViewModel.updateCustomHeaders(updatedList) + if (updatedList.isEmpty()) { + updatedList.add(ServerCustomHeader.empty()) } - ) - } + + settingsViewModel.updateCustomHeaders(updatedList) + } + ) + } } }, floatingActionButtonPosition = FabPosition.Center, @@ -105,12 +107,10 @@ fun CustomHeadersSettingsScreen( onClick = { val updatedList = headers.value.toMutableList() updatedList.add(ServerCustomHeader.empty()) - settingsViewModel.updateCustomHeaders(updatedList) } ) { Icon( - imageVector = Icons.Filled.Add, contentDescription = "Add" ) From ad107f00ad30847037d986ebbbcf5a0adeaa1d15 Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 01:25:29 +0100 Subject: [PATCH 35/59] wip --- .../advanced/CustomHeaderComposable.kt | 84 +++++++++++-------- .../advanced/CustomHeadersSettingsScreen.kt | 1 + app/src/main/res/values-ru/strings.xml | 2 +- 3 files changed, 51 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt index 929369f5..14a71789 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt @@ -1,23 +1,32 @@ package org.grakovne.lissen.ui.screens.settings.advanced +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.DeleteOutline +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import org.grakovne.lissen.R @@ -29,48 +38,53 @@ fun CustomHeaderComposable( onChanged: (ServerCustomHeader) -> Unit, onDelete: (ServerCustomHeader) -> Unit ) { - Row( + + Card( + shape = RoundedCornerShape(12.dp), modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), - verticalAlignment = Alignment.CenterVertically + elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) ) { - Column( - modifier = Modifier.weight(1f) + Row( + modifier = Modifier + .fillMaxWidth() + .background(colorScheme.surfaceContainer) + .padding(start = 16.dp, end = 0.dp, top = 16.dp, bottom = 16.dp), + verticalAlignment = Alignment.CenterVertically ) { - OutlinedTextField( - value = header.name, - onValueChange = { onChanged(header.copy(name = it, value = header.value)) }, - label = { Text(stringResource(R.string.custom_header_hint_name)) }, - singleLine = true, - shape = RoundedCornerShape(16.dp), - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 4.dp) - ) - - OutlinedTextField( - value = header.value, - onValueChange = { onChanged(header.copy(name = header.name, value = it)) }, - label = { Text(stringResource(R.string.custom_header_hint_value)) }, - singleLine = true, - shape = RoundedCornerShape(16.dp), - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 4.dp) - ) - } + Column( + modifier = Modifier.weight(1f) + ) { + OutlinedTextField( + value = header.name, + onValueChange = { onChanged(header.copy(name = it, value = header.value)) }, + label = { Text(stringResource(R.string.custom_header_hint_name)) }, + singleLine = true, + shape = RoundedCornerShape(16.dp), + modifier = Modifier.fillMaxWidth() + ) - Spacer(modifier = Modifier.width(16.dp)) + OutlinedTextField( + value = header.value, + onValueChange = { onChanged(header.copy(name = header.name, value = it)) }, + label = { Text(stringResource(R.string.custom_header_hint_value)) }, + singleLine = true, + shape = RoundedCornerShape(16.dp), + modifier = Modifier.fillMaxWidth() + ) + } - IconButton( - onClick = { onDelete(header) } - ) { - Icon( - imageVector = Icons.Default.DeleteOutline, - contentDescription = null, - modifier = Modifier.size(36.dp) - ) + IconButton( + onClick = { onDelete(header) } + ) { + Icon( + imageVector = Icons.Default.DeleteOutline, + contentDescription = null, + tint = colorScheme.error, + modifier = Modifier.size(24.dp) + ) + } } } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 278e4572..9cb47e65 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowBack import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.outlined.Add import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FabPosition import androidx.compose.material3.FloatingActionButton diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 854bd263..7837f1dd 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -48,7 +48,7 @@ Поиск по автору или названию Сейчас играет Прокси-заголовки - Заголовки для подключения к серверу + Заголовки Ключ Значение Прокси-заголовки From 2e566dc6a053ba4a7607fc2968bdb02a655ce6c1 Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 01:58:12 +0100 Subject: [PATCH 36/59] wip --- .../settings/advanced/CustomHeaderComposable.kt | 16 +++------------- .../advanced/CustomHeadersSettingsScreen.kt | 11 +++++++++++ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt index 14a71789..92991725 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt @@ -1,32 +1,25 @@ package org.grakovne.lissen.ui.screens.settings.advanced import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.DeleteOutline import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import org.grakovne.lissen.R @@ -38,19 +31,17 @@ fun CustomHeaderComposable( onChanged: (ServerCustomHeader) -> Unit, onDelete: (ServerCustomHeader) -> Unit ) { - Card( shape = RoundedCornerShape(12.dp), modifier = Modifier .fillMaxWidth() - .padding(horizontal = 16.dp), - elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) + .padding(horizontal = 16.dp) ) { Row( modifier = Modifier .fillMaxWidth() - .background(colorScheme.surfaceContainer) - .padding(start = 16.dp, end = 0.dp, top = 16.dp, bottom = 16.dp), + .background(colorScheme.background) + .padding(start = 16.dp, end = 0.dp, top = 0.dp, bottom = 8.dp), verticalAlignment = Alignment.CenterVertically ) { Column( @@ -88,5 +79,4 @@ fun CustomHeaderComposable( } } - Spacer(modifier = Modifier.height(16.dp)) } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 9cb47e65..b25adb11 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -3,6 +3,9 @@ package org.grakovne.lissen.ui.screens.settings.advanced import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed @@ -14,6 +17,7 @@ import androidx.compose.material.icons.outlined.Add import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FabPosition import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme.colorScheme @@ -97,6 +101,13 @@ fun CustomHeadersSettingsScreen( settingsViewModel.updateCustomHeaders(updatedList) } ) + + if (index < headers.value.size - 1) { + HorizontalDivider(modifier = Modifier + .height(1.dp) + .padding(horizontal = 24.dp) + ) + } } } }, From d8d0ac6e982c44093efa61b0ff99d67feda76677 Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 02:09:04 +0100 Subject: [PATCH 37/59] wip --- .../advanced/CustomHeaderComposable.kt | 10 +++------- .../advanced/CustomHeadersSettingsScreen.kt | 20 +++++++++++++++---- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt index 92991725..f8ba6590 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt @@ -3,9 +3,7 @@ package org.grakovne.lissen.ui.screens.settings.advanced import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape @@ -35,13 +33,12 @@ fun CustomHeaderComposable( shape = RoundedCornerShape(12.dp), modifier = Modifier .fillMaxWidth() - .padding(horizontal = 16.dp) + .padding(start = 16.dp, end = 8.dp, top = 8.dp, bottom = 16.dp) ) { Row( modifier = Modifier .fillMaxWidth() - .background(colorScheme.background) - .padding(start = 16.dp, end = 0.dp, top = 0.dp, bottom = 8.dp), + .background(colorScheme.background), verticalAlignment = Alignment.CenterVertically ) { Column( @@ -73,10 +70,9 @@ fun CustomHeaderComposable( imageVector = Icons.Default.DeleteOutline, contentDescription = null, tint = colorScheme.error, - modifier = Modifier.size(24.dp) + modifier = Modifier.size(28.dp) ) } } } - } diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index b25adb11..33f5b1f1 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -4,11 +4,11 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowBack @@ -27,15 +27,18 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import kotlinx.coroutines.launch import org.grakovne.lissen.R import org.grakovne.lissen.domain.connection.ServerCustomHeader import org.grakovne.lissen.viewmodel.SettingsViewModel +import kotlin.math.max @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -48,6 +51,9 @@ fun CustomHeadersSettingsScreen( val fabHeight = 56.dp val additionalPadding = 16.dp + val state = rememberLazyListState() + val coroutineScope = rememberCoroutineScope() + Scaffold( topBar = { TopAppBar( @@ -74,6 +80,7 @@ fun CustomHeadersSettingsScreen( .fillMaxHeight(), content = { innerPadding -> LazyColumn( + state = state, contentPadding = PaddingValues( top = innerPadding.calculateTopPadding(), bottom = innerPadding.calculateBottomPadding() + fabHeight + additionalPadding @@ -103,9 +110,10 @@ fun CustomHeadersSettingsScreen( ) if (index < headers.value.size - 1) { - HorizontalDivider(modifier = Modifier - .height(1.dp) - .padding(horizontal = 24.dp) + HorizontalDivider( + modifier = Modifier + .height(1.dp) + .padding(horizontal = 24.dp) ) } } @@ -120,6 +128,10 @@ fun CustomHeadersSettingsScreen( val updatedList = headers.value.toMutableList() updatedList.add(ServerCustomHeader.empty()) settingsViewModel.updateCustomHeaders(updatedList) + + coroutineScope.launch { + state.scrollToItem(max(0, updatedList.size - 1)) + } } ) { Icon( From 86b94af4cd67e5bdb743c1657127024603db2199 Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 02:16:13 +0100 Subject: [PATCH 38/59] wip --- .../settings/advanced/CustomHeaderComposable.kt | 2 +- .../advanced/CustomHeadersSettingsScreen.kt | 13 ++++++++++--- .../grakovne/lissen/viewmodel/SettingsViewModel.kt | 10 +++++++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt index f8ba6590..ded4bc9a 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt @@ -50,7 +50,7 @@ fun CustomHeaderComposable( label = { Text(stringResource(R.string.custom_header_hint_name)) }, singleLine = true, shape = RoundedCornerShape(16.dp), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp) ) OutlinedTextField( diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 33f5b1f1..90e6d584 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -13,7 +13,6 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowBack import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.outlined.Add import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FabPosition import androidx.compose.material3.FloatingActionButton @@ -65,7 +64,10 @@ fun CustomHeadersSettingsScreen( ) }, navigationIcon = { - IconButton(onClick = onBack) { + IconButton(onClick = { + onBack() + settingsViewModel.saveCustomHeaders() + }) { Icon( imageVector = Icons.AutoMirrored.Outlined.ArrowBack, contentDescription = "Back", @@ -88,7 +90,12 @@ fun CustomHeadersSettingsScreen( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally ) { - itemsIndexed(headers.value) { index, header -> + val customHeaders = when (headers.value.isEmpty()) { + true -> listOf(ServerCustomHeader.empty()) + false -> headers.value + } + + itemsIndexed(customHeaders) { index, header -> CustomHeaderComposable( header = header, onChanged = { newPair -> diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt index c0b193a0..c349c3ef 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt @@ -81,7 +81,15 @@ class SettingsViewModel @Inject constructor( fun updateCustomHeaders(headers: List) { _customHeaders.value = headers - preferences.saveCustomHeaders(headers) + } + + fun saveCustomHeaders() { + _customHeaders.value = _customHeaders + .value + ?.filterNot { it.name.isEmpty() } + ?.filterNot { it.value.isEmpty() } + + preferences.saveCustomHeaders(_customHeaders.value ?: emptyList()) } private fun fetchCustomHeaders(): List = preferences From 1b5350341ea0d1e741002a2647ccc7473a308204 Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 02:29:23 +0100 Subject: [PATCH 39/59] wip --- .../settings/advanced/CustomHeadersSettingsScreen.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 90e6d584..549b99d9 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -99,13 +99,13 @@ fun CustomHeadersSettingsScreen( CustomHeaderComposable( header = header, onChanged = { newPair -> - val updatedList = headers.value.toMutableList() + val updatedList = customHeaders.toMutableList() updatedList[index] = newPair settingsViewModel.updateCustomHeaders(updatedList) }, onDelete = { pair -> - val updatedList = headers.value.toMutableList() + val updatedList = customHeaders.toMutableList() updatedList.remove(pair) if (updatedList.isEmpty()) { @@ -116,7 +116,7 @@ fun CustomHeadersSettingsScreen( } ) - if (index < headers.value.size - 1) { + if (index < customHeaders.size - 1) { HorizontalDivider( modifier = Modifier .height(1.dp) From bc1b1305d45cb28ccbfe09f48177c0c99d6e9bfd Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 12:44:05 +0100 Subject: [PATCH 40/59] wip --- .../audiobookshelf/AudiobookshelfChannel.kt | 43 +++++++++++-------- .../lissen/channel/common/MediaChannel.kt | 5 ++- .../lissen/content/LissenMediaProvider.kt | 5 ++- .../content/cache/BookCachingService.kt | 4 +- .../content/cache/LocalCacheRepository.kt | 7 ++- .../org/grakovne/lissen/domain/RequestUri.kt | 9 ++++ .../playback/service/PlaybackService.kt | 6 ++- 7 files changed, 53 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt index cf37f534..ad03e2bd 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt @@ -25,6 +25,7 @@ import org.grakovne.lissen.domain.PagedItems import org.grakovne.lissen.domain.PlaybackProgress import org.grakovne.lissen.domain.PlaybackSession import org.grakovne.lissen.domain.RecentBook +import org.grakovne.lissen.domain.RequestUri import org.grakovne.lissen.domain.UserAccount import org.grakovne.lissen.persistence.preferences.LissenSharedPreferences import java.io.InputStream @@ -50,26 +51,34 @@ class AudiobookshelfChannel @Inject constructor( override fun provideFileUri( libraryItemId: String, fileId: String - ): Uri = Uri.parse(preferences.getHost()) - .buildUpon() - .appendPath("api") - .appendPath("items") - .appendPath(libraryItemId) - .appendPath("file") - .appendPath(fileId) - .appendQueryParameter("token", preferences.getToken()) - .build() + ): RequestUri { + val uri = Uri.parse(preferences.getHost()) + .buildUpon() + .appendPath("api") + .appendPath("items") + .appendPath(libraryItemId) + .appendPath("file") + .appendPath(fileId) + .appendQueryParameter("token", preferences.getToken()) + .build() + + return RequestUri(uri = uri) + } override fun provideBookCoverUri( bookId: String - ): Uri = Uri.parse(preferences.getHost()) - .buildUpon() - .appendPath("api") - .appendPath("items") - .appendPath(bookId) - .appendPath("cover") - .appendQueryParameter("token", preferences.getToken()) - .build() + ): RequestUri { + val uri = Uri.parse(preferences.getHost()) + .buildUpon() + .appendPath("api") + .appendPath("items") + .appendPath(bookId) + .appendPath("cover") + .appendQueryParameter("token", preferences.getToken()) + .build() + + return RequestUri(uri = uri) + } override suspend fun syncProgress( sessionId: String, diff --git a/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt b/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt index f59d11ed..61d3878b 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt @@ -8,6 +8,7 @@ import org.grakovne.lissen.domain.PagedItems import org.grakovne.lissen.domain.PlaybackProgress import org.grakovne.lissen.domain.PlaybackSession import org.grakovne.lissen.domain.RecentBook +import org.grakovne.lissen.domain.RequestUri import org.grakovne.lissen.domain.UserAccount import java.io.InputStream @@ -18,11 +19,11 @@ interface MediaChannel { fun provideFileUri( libraryItemId: String, fileId: String - ): Uri + ): RequestUri fun provideBookCoverUri( bookId: String - ): Uri + ): RequestUri suspend fun syncProgress( sessionId: String, diff --git a/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt b/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt index 21717eba..7818f848 100644 --- a/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt +++ b/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt @@ -16,6 +16,7 @@ import org.grakovne.lissen.domain.PagedItems import org.grakovne.lissen.domain.PlaybackProgress import org.grakovne.lissen.domain.PlaybackSession import org.grakovne.lissen.domain.RecentBook +import org.grakovne.lissen.domain.RequestUri import org.grakovne.lissen.domain.UserAccount import org.grakovne.lissen.persistence.preferences.LissenSharedPreferences import java.io.InputStream @@ -33,7 +34,7 @@ class LissenMediaProvider @Inject constructor( fun provideFileUri( libraryItemId: String, chapterId: String - ): ApiResult { + ): ApiResult { Log.d(TAG, "Fetching File $libraryItemId and $chapterId URI") return when (cacheConfiguration.localCacheUsing()) { @@ -57,7 +58,7 @@ class LissenMediaProvider @Inject constructor( fun provideBookCoverUri( bookId: String - ): Uri { + ): RequestUri { Log.d(TAG, "Fetching Cover URI for $bookId") return when (cacheConfiguration.localCacheUsing()) { diff --git a/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt b/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt index 9a365aff..73616003 100644 --- a/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt +++ b/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt @@ -88,7 +88,9 @@ class BookCachingService @Inject constructor( val downloads = book .files .map { file -> - Request(channel.provideFileUri(book.id, file.id)) + + // here + Request(channel.provideFileUri(book.id, file.id).uri) .setTitle(file.name) .setNotificationVisibility(VISIBILITY_VISIBLE) .setDestinationUri(properties.provideMediaCachePatch(book.id, file.id).toUri()) diff --git a/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt b/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt index ea0a709b..76e72898 100644 --- a/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt @@ -13,6 +13,7 @@ import org.grakovne.lissen.domain.PagedItems import org.grakovne.lissen.domain.PlaybackProgress import org.grakovne.lissen.domain.PlaybackSession import org.grakovne.lissen.domain.RecentBook +import org.grakovne.lissen.domain.RequestUri import java.io.InputStream import javax.inject.Inject import javax.inject.Singleton @@ -24,16 +25,18 @@ class LocalCacheRepository @Inject constructor( private val properties: CacheBookStorageProperties ) { - fun provideFileUri(libraryItemId: String, fileId: String): Uri? = + fun provideFileUri(libraryItemId: String, fileId: String): RequestUri? = cachedBookRepository .provideFileUri(libraryItemId, fileId) .takeIf { it.toFile().exists() } + ?.let { RequestUri(uri = it) } - fun provideBookCover(bookId: String): Uri = + fun provideBookCover(bookId: String): RequestUri = cachedBookRepository .provideBookCover(bookId) .toString() .let { parse(it) } + .let { RequestUri(uri = it) } /** * For the local cache we avoiding to create intermediary entity like Session and using BookId diff --git a/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt b/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt new file mode 100644 index 00000000..2ce61ea0 --- /dev/null +++ b/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt @@ -0,0 +1,9 @@ +package org.grakovne.lissen.domain + +import android.net.Uri +import org.grakovne.lissen.domain.connection.ServerCustomHeader + +data class RequestUri ( + val uri: Uri, + val headers: List = emptyList() +) \ No newline at end of file diff --git a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt index c411f68e..cb0411fc 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt @@ -129,13 +129,15 @@ class PlaybackService : MediaSessionService() { onSuccess = { MediaItem.Builder() .setMediaId(file.id) - .setUri(it) + // here + .setUri(it.uri) .setTag(book) .setMediaMetadata( MediaMetadata.Builder() .setTitle(file.name) .setArtist(book.title) - .setArtworkUri(coverUri) + // here + .setArtworkUri(coverUri.uri) .build() ) .build() From 3fce8823c22d9872ffc191ad2ce3422340b19487 Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 12:48:47 +0100 Subject: [PATCH 41/59] wip --- .../api/AudioBookshelfDataRepository.kt | 13 +++++++---- .../api/AudioBookshelfMediaRepository.kt | 7 +++--- .../api/RequestHeadersProvider.kt | 22 +++++++++++++++++++ .../lissen/channel/common/MediaChannel.kt | 1 - .../lissen/content/LissenMediaProvider.kt | 1 - .../content/cache/LocalCacheRepository.kt | 1 - .../org/grakovne/lissen/domain/RequestUri.kt | 4 ++-- 7 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/RequestHeadersProvider.kt diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt index 0d83ed95..a0232a19 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt @@ -27,7 +27,8 @@ import javax.inject.Singleton @Singleton class AudioBookshelfDataRepository @Inject constructor( private val loginResponseConverter: LoginResponseConverter, - private val preferences: LissenSharedPreferences + private val preferences: LissenSharedPreferences, + private val requestHeadersProvider: RequestHeadersProvider ) { private var configCache: ApiClientConfig? = null @@ -108,7 +109,11 @@ class AudioBookshelfDataRepository @Inject constructor( lateinit var apiService: AudiobookshelfApiClient try { - val apiClient = ApiClient(host = host, customHeaders = preferences.getCustomHeaders()) + val apiClient = ApiClient( + host = host, + customHeaders = requestHeadersProvider.fetchRequestHeaders() + ) + apiService = apiClient.retrofit.create(AudiobookshelfApiClient::class.java) } catch (e: Exception) { return ApiResult.Error(ApiError.InternalError) @@ -161,7 +166,7 @@ class AudioBookshelfDataRepository @Inject constructor( val cache = ApiClientConfig( host = host, token = token, - customHeaders = preferences.getCustomHeaders() + customHeaders = requestHeadersProvider.fetchRequestHeaders() ) val currentClientCache = clientCache @@ -197,7 +202,7 @@ class AudioBookshelfDataRepository @Inject constructor( ): ApiClient = ApiClient( host = host, token = token, - customHeaders = preferences.getCustomHeaders() + customHeaders = requestHeadersProvider.fetchRequestHeaders() ) companion object { diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt index b6468f8b..d019d991 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt @@ -14,7 +14,8 @@ import javax.inject.Singleton @Singleton class AudioBookshelfMediaRepository @Inject constructor( - private val preferences: LissenSharedPreferences + private val preferences: LissenSharedPreferences, + private val requestHeadersProvider: RequestHeadersProvider ) { private var configCache: ApiClientConfig? = null @@ -56,7 +57,7 @@ class AudioBookshelfMediaRepository @Inject constructor( val cache = ApiClientConfig( host = host, token = token, - customHeaders = preferences.getCustomHeaders() + customHeaders = requestHeadersProvider.fetchRequestHeaders() ) val currentClientCache = clientCache @@ -92,6 +93,6 @@ class AudioBookshelfMediaRepository @Inject constructor( ): BinaryApiClient = BinaryApiClient( host = host, token = token, - customHeaders = preferences.getCustomHeaders() + customHeaders = requestHeadersProvider.fetchRequestHeaders() ) } diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/RequestHeadersProvider.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/RequestHeadersProvider.kt new file mode 100644 index 00000000..bb2a43ad --- /dev/null +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/RequestHeadersProvider.kt @@ -0,0 +1,22 @@ +package org.grakovne.lissen.channel.audiobookshelf.api + +import org.grakovne.lissen.channel.common.USER_AGENT +import org.grakovne.lissen.domain.connection.ServerCustomHeader +import org.grakovne.lissen.persistence.preferences.LissenSharedPreferences +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RequestHeadersProvider @Inject constructor( + private val preferences: LissenSharedPreferences +) { + + fun fetchRequestHeaders(): List { + val usersHeaders = preferences + .getCustomHeaders() + ?: emptyList() + + val userAgent = ServerCustomHeader("User-Agent", USER_AGENT) + return usersHeaders + userAgent + } +} diff --git a/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt b/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt index 61d3878b..b1cbadd2 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt @@ -1,6 +1,5 @@ package org.grakovne.lissen.channel.common -import android.net.Uri import org.grakovne.lissen.domain.Book import org.grakovne.lissen.domain.DetailedBook import org.grakovne.lissen.domain.Library diff --git a/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt b/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt index 7818f848..858f5953 100644 --- a/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt +++ b/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt @@ -1,6 +1,5 @@ package org.grakovne.lissen.content -import android.net.Uri import android.util.Log import org.grakovne.lissen.channel.common.ApiError import org.grakovne.lissen.channel.common.ApiResult diff --git a/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt b/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt index 76e72898..0289e889 100644 --- a/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt @@ -1,6 +1,5 @@ package org.grakovne.lissen.content.cache -import android.net.Uri import android.net.Uri.parse import androidx.core.net.toFile import org.grakovne.lissen.channel.common.ApiError diff --git a/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt b/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt index 2ce61ea0..65a3c1f2 100644 --- a/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt +++ b/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt @@ -3,7 +3,7 @@ package org.grakovne.lissen.domain import android.net.Uri import org.grakovne.lissen.domain.connection.ServerCustomHeader -data class RequestUri ( +data class RequestUri( val uri: Uri, val headers: List = emptyList() -) \ No newline at end of file +) From 57f75b4dfcef3d6379a9b71f367362310e1439aa Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 12:49:57 +0100 Subject: [PATCH 42/59] wip --- .../audiobookshelf/AudiobookshelfChannel.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt index ad03e2bd..e937eed8 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.coroutineScope import org.grakovne.lissen.channel.audiobookshelf.api.AudioBookshelfDataRepository import org.grakovne.lissen.channel.audiobookshelf.api.AudioBookshelfMediaRepository import org.grakovne.lissen.channel.audiobookshelf.api.AudioBookshelfSyncService +import org.grakovne.lissen.channel.audiobookshelf.api.RequestHeadersProvider import org.grakovne.lissen.channel.audiobookshelf.converter.LibraryItemIdResponseConverter import org.grakovne.lissen.channel.audiobookshelf.converter.LibraryItemResponseConverter import org.grakovne.lissen.channel.audiobookshelf.converter.LibraryResponseConverter @@ -43,7 +44,8 @@ class AudiobookshelfChannel @Inject constructor( private val sessionResponseConverter: PlaybackSessionResponseConverter, private val librarySearchItemsConverter: LibrarySearchItemsConverter, private val preferences: LissenSharedPreferences, - private val syncService: AudioBookshelfSyncService + private val syncService: AudioBookshelfSyncService, + private val requestHeadersProvider: RequestHeadersProvider ) : MediaChannel { override fun getChannelCode() = ChannelCode.AUDIOBOOKSHELF @@ -62,7 +64,10 @@ class AudiobookshelfChannel @Inject constructor( .appendQueryParameter("token", preferences.getToken()) .build() - return RequestUri(uri = uri) + return RequestUri( + uri = uri, + headers = requestHeadersProvider.fetchRequestHeaders() + ) } override fun provideBookCoverUri( @@ -77,7 +82,10 @@ class AudiobookshelfChannel @Inject constructor( .appendQueryParameter("token", preferences.getToken()) .build() - return RequestUri(uri = uri) + return RequestUri( + uri = uri, + headers = requestHeadersProvider.fetchRequestHeaders() + ) } override suspend fun syncProgress( From 18cd4381a21d56ccffba0236467ddc5ab4ab49b3 Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 13:30:10 +0100 Subject: [PATCH 43/59] wip --- .../playback/service/PlaybackService.kt | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt index cb0411fc..58b13ab3 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt @@ -6,8 +6,12 @@ import androidx.annotation.OptIn import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.media3.common.MediaItem import androidx.media3.common.MediaMetadata +import androidx.media3.common.MediaMetadata.PICTURE_TYPE_FRONT_COVER +import androidx.media3.common.MediaMetadata.PictureType import androidx.media3.common.util.UnstableApi +import androidx.media3.datasource.DefaultHttpDataSource import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.exoplayer.source.ProgressiveMediaSource import androidx.media3.session.MediaSession import androidx.media3.session.MediaSessionService import dagger.hilt.android.AndroidEntryPoint @@ -23,6 +27,7 @@ import org.grakovne.lissen.domain.BookFile import org.grakovne.lissen.domain.DetailedBook import org.grakovne.lissen.domain.MediaProgress import org.grakovne.lissen.persistence.preferences.LissenSharedPreferences +import java.io.InputStream import javax.inject.Inject @AndroidEntryPoint @@ -117,37 +122,60 @@ class PlaybackService : MediaSessionService() { withContext(Dispatchers.IO) { val prepareQueue = async { + + // remove me val coverUri = mediaChannel.provideBookCoverUri(book.id) + val cover: ByteArray? = channelProvider + .fetchBookCover(bookId = book.id) + .fold( + onSuccess = { + try { + it.readBytes() + } catch (ex: Exception) { + null + } + }, + onFailure = { null } + ) + val playingQueue = book .files .mapNotNull { file -> - mediaChannel .provideFileUri(book.id, file.id) .fold( - onSuccess = { - MediaItem.Builder() + onSuccess = { request -> + val dataSourceFactory = DefaultHttpDataSource + .Factory() + .setDefaultRequestProperties(request + .headers + .associate { it.name to it.value } + ) + + val mediaData = MediaMetadata.Builder() + .setTitle(file.name) + .setArtist(book.title) + + cover?.let { mediaData.setArtworkData(it, PICTURE_TYPE_FRONT_COVER) } + + val mediaItem = MediaItem.Builder() .setMediaId(file.id) - // here - .setUri(it.uri) + .setUri(request.uri) .setTag(book) - .setMediaMetadata( - MediaMetadata.Builder() - .setTitle(file.name) - .setArtist(book.title) - // here - .setArtworkUri(coverUri.uri) - .build() - ) + .setMediaMetadata(mediaData.build()) .build() + + ProgressiveMediaSource + .Factory(dataSourceFactory) + .createMediaSource(mediaItem) }, onFailure = { null } ) } withContext(Dispatchers.Main) { - exoPlayer.setMediaItems(playingQueue) + exoPlayer.setMediaSources(playingQueue) setPlaybackProgress(book.files, book.progress) } } From 9b42dd41010a2baf8eec2a73b6a96b9a95c68e3c Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 13:56:34 +0100 Subject: [PATCH 44/59] wip --- .../lissen/content/cache/BookCachingService.kt | 12 ++++++++---- .../lissen/playback/service/PlaybackService.kt | 18 +++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt b/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt index 73616003..685795be 100644 --- a/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt +++ b/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt @@ -88,15 +88,19 @@ class BookCachingService @Inject constructor( val downloads = book .files .map { file -> - - // here - Request(channel.provideFileUri(book.id, file.id).uri) + val serverRequest = channel.provideFileUri(book.id, file.id) + val downloadRequest = Request(serverRequest.uri) .setTitle(file.name) .setNotificationVisibility(VISIBILITY_VISIBLE) .setDestinationUri(properties.provideMediaCachePatch(book.id, file.id).toUri()) .setAllowedOverMetered(true) .setAllowedOverRoaming(true) - .let { downloadManager.enqueue(it) } + + serverRequest + .headers + .forEach { downloadRequest.addRequestHeader(it.name, it.value) } + + downloadRequest.let { downloadManager.enqueue(it) } } return awaitDownloadProgress(downloads, downloadManager) diff --git a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt index 58b13ab3..6ffba4d7 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt @@ -7,8 +7,8 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.media3.common.MediaItem import androidx.media3.common.MediaMetadata import androidx.media3.common.MediaMetadata.PICTURE_TYPE_FRONT_COVER -import androidx.media3.common.MediaMetadata.PictureType import androidx.media3.common.util.UnstableApi +import androidx.media3.datasource.DefaultDataSource import androidx.media3.datasource.DefaultHttpDataSource import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.source.ProgressiveMediaSource @@ -27,7 +27,6 @@ import org.grakovne.lissen.domain.BookFile import org.grakovne.lissen.domain.DetailedBook import org.grakovne.lissen.domain.MediaProgress import org.grakovne.lissen.persistence.preferences.LissenSharedPreferences -import java.io.InputStream import javax.inject.Inject @AndroidEntryPoint @@ -122,7 +121,6 @@ class PlaybackService : MediaSessionService() { withContext(Dispatchers.IO) { val prepareQueue = async { - // remove me val coverUri = mediaChannel.provideBookCoverUri(book.id) @@ -146,13 +144,19 @@ class PlaybackService : MediaSessionService() { .provideFileUri(book.id, file.id) .fold( onSuccess = { request -> - val dataSourceFactory = DefaultHttpDataSource + val httpDataSourceFactory = DefaultHttpDataSource .Factory() - .setDefaultRequestProperties(request - .headers - .associate { it.name to it.value } + .setDefaultRequestProperties( + request + .headers + .associate { it.name to it.value } ) + val dataSourceFactory = DefaultDataSource.Factory( + baseContext, + httpDataSourceFactory + ) + val mediaData = MediaMetadata.Builder() .setTitle(file.name) .setArtist(book.title) From 44b369ea3025f13b2684346752ae81bd4eabacb7 Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 13:59:31 +0100 Subject: [PATCH 45/59] wip --- .../audiobookshelf/AudiobookshelfChannel.kt | 18 ------------------ .../lissen/channel/common/MediaChannel.kt | 4 ---- .../lissen/content/LissenMediaProvider.kt | 11 ----------- .../content/cache/LocalCacheRepository.kt | 8 -------- .../lissen/playback/service/PlaybackService.kt | 1 - 5 files changed, 42 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt index e937eed8..73e27329 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt @@ -70,24 +70,6 @@ class AudiobookshelfChannel @Inject constructor( ) } - override fun provideBookCoverUri( - bookId: String - ): RequestUri { - val uri = Uri.parse(preferences.getHost()) - .buildUpon() - .appendPath("api") - .appendPath("items") - .appendPath(bookId) - .appendPath("cover") - .appendQueryParameter("token", preferences.getToken()) - .build() - - return RequestUri( - uri = uri, - headers = requestHeadersProvider.fetchRequestHeaders() - ) - } - override suspend fun syncProgress( sessionId: String, progress: PlaybackProgress diff --git a/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt b/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt index b1cbadd2..b8424978 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt @@ -20,10 +20,6 @@ interface MediaChannel { fileId: String ): RequestUri - fun provideBookCoverUri( - bookId: String - ): RequestUri - suspend fun syncProgress( sessionId: String, progress: PlaybackProgress diff --git a/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt b/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt index 858f5953..ccdc3592 100644 --- a/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt +++ b/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt @@ -55,17 +55,6 @@ class LissenMediaProvider @Inject constructor( } } - fun provideBookCoverUri( - bookId: String - ): RequestUri { - Log.d(TAG, "Fetching Cover URI for $bookId") - - return when (cacheConfiguration.localCacheUsing()) { - true -> localCacheRepository.provideBookCover(bookId) - false -> providePreferredChannel().provideBookCoverUri(bookId) - } - } - suspend fun syncProgress( sessionId: String, bookId: String, diff --git a/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt b/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt index 0289e889..618f8d0f 100644 --- a/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt @@ -1,6 +1,5 @@ package org.grakovne.lissen.content.cache -import android.net.Uri.parse import androidx.core.net.toFile import org.grakovne.lissen.channel.common.ApiError import org.grakovne.lissen.channel.common.ApiResult @@ -30,13 +29,6 @@ class LocalCacheRepository @Inject constructor( .takeIf { it.toFile().exists() } ?.let { RequestUri(uri = it) } - fun provideBookCover(bookId: String): RequestUri = - cachedBookRepository - .provideBookCover(bookId) - .toString() - .let { parse(it) } - .let { RequestUri(uri = it) } - /** * For the local cache we avoiding to create intermediary entity like Session and using BookId * as a Playback Session Key diff --git a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt index 6ffba4d7..68d9c4a5 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt @@ -122,7 +122,6 @@ class PlaybackService : MediaSessionService() { withContext(Dispatchers.IO) { val prepareQueue = async { // remove me - val coverUri = mediaChannel.provideBookCoverUri(book.id) val cover: ByteArray? = channelProvider .fetchBookCover(bookId = book.id) From 18c959c9b961634cee80ac9abfede8c682210a77 Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 15:33:22 +0100 Subject: [PATCH 46/59] wip --- app/src/main/res/values-ru/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 7837f1dd..854bd263 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -48,7 +48,7 @@ Поиск по автору или названию Сейчас играет Прокси-заголовки - Заголовки + Заголовки для подключения к серверу Ключ Значение Прокси-заголовки From 8343e2eb5918856ba1aa52523d2af1a5402b0d2a Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 18:35:45 +0100 Subject: [PATCH 47/59] wip --- .../audiobookshelf/AudiobookshelfChannel.kt | 4 +++- .../audiobookshelf/model/LibraryResponse.kt | 14 +------------- .../channel/audiobookshelf/model/LoginResponse.kt | 7 +------ .../audiobookshelf/model/MediaMetadataResponse.kt | 12 +----------- 4 files changed, 6 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt index 73e27329..fe04d014 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt @@ -184,7 +184,9 @@ class AudiobookshelfChannel @Inject constructor( onFailure = { Success(libraryItemIdResponseConverter.apply(item, null)) } ) }, - onFailure = { ApiResult.Error(it.code) } + onFailure = { + ApiResult.Error(it.code) + } ) } diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/model/LibraryResponse.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/model/LibraryResponse.kt index e43dba76..51546d2d 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/model/LibraryResponse.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/model/LibraryResponse.kt @@ -7,17 +7,5 @@ data class LibraryResponse( data class Library( val id: String, val name: String, - val folders: List, - val displayOrder: Int, - val icon: String, - val mediaType: String, - val provider: String, - val createdAt: Long, - val lastUpdate: Long -) - -data class Folder( - val id: String, - val fullPath: String, - val libraryId: String + val mediaType: String ) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/model/LoginResponse.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/model/LoginResponse.kt index b4f5380b..c17d8ac3 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/model/LoginResponse.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/model/LoginResponse.kt @@ -7,10 +7,5 @@ data class LoginResponse( data class User( val id: String, - val username: String, - val email: String?, - val type: String, - val token: String, - val isActive: Boolean, - val isLocked: Boolean + val token: String ) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/model/MediaMetadataResponse.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/model/MediaMetadataResponse.kt index 3615bdab..6dcd9a92 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/model/MediaMetadataResponse.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/model/MediaMetadataResponse.kt @@ -2,17 +2,7 @@ package org.grakovne.lissen.channel.audiobookshelf.model data class MediaMetadataResponse( val title: String, - val subtitle: String?, - val authors: List?, - val publishedYear: Int?, - val publishedDate: String?, - val publisher: String?, - val description: String?, - val isbn: String?, - val asin: String?, - val language: String?, - val explicit: Boolean, - val abridged: Boolean + val authors: List? ) data class Author( From 39699b567dfc51d29b3a45a04b06497892a1e8de Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 19:11:34 +0100 Subject: [PATCH 48/59] wip --- .../main/java/org/grakovne/lissen/playback/MediaModule.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/playback/MediaModule.kt b/app/src/main/java/org/grakovne/lissen/playback/MediaModule.kt index d644ac57..e03bafe4 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/MediaModule.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/MediaModule.kt @@ -9,6 +9,7 @@ import androidx.media3.common.C import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.DefaultLoadControl import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.exoplayer.LoadControl import androidx.media3.session.MediaSession import dagger.Module import dagger.Provides @@ -30,12 +31,6 @@ object MediaModule { .setSeekBackIncrementMs(10_000) .setSeekForwardIncrementMs(30_000) .setHandleAudioBecomingNoisy(true) - .setLoadControl( - DefaultLoadControl - .Builder() - .setPrioritizeTimeOverSizeThresholds(true) - .build() - ) .setAudioAttributes( AudioAttributes.Builder() .setUsage(C.USAGE_MEDIA) From 9903c224714e2af77338d2f12f04d918889f2a96 Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 19:26:29 +0100 Subject: [PATCH 49/59] wip --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6aaa3520..961ad5bb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -25,8 +25,8 @@ android { applicationId = "org.grakovne.lissen" minSdk = 28 targetSdk = 35 - versionCode = 23 - versionName = "1.0.22-dev" + versionCode = 24 + versionName = "1.0.23" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { From b5944367148c59de7dfd6c54ec110dbe1dce84eb Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 19:32:35 +0100 Subject: [PATCH 50/59] wip --- .../org/grakovne/lissen/playback/service/PlaybackService.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt index 68d9c4a5..238e81b0 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt @@ -121,8 +121,6 @@ class PlaybackService : MediaSessionService() { withContext(Dispatchers.IO) { val prepareQueue = async { - // remove me - val cover: ByteArray? = channelProvider .fetchBookCover(bookId = book.id) .fold( From 77cb9a02964464f2e642a651586fa3ea1af9d92c Mon Sep 17 00:00:00 2001 From: grakovne Date: Wed, 6 Nov 2024 20:25:34 +0100 Subject: [PATCH 51/59] wip --- .../main/java/org/grakovne/lissen/channel/common/UserAgent.kt | 3 ++- .../org/grakovne/lissen/playback/service/PlaybackService.kt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/grakovne/lissen/channel/common/UserAgent.kt b/app/src/main/java/org/grakovne/lissen/channel/common/UserAgent.kt index b4628328..6cf6c0fc 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/common/UserAgent.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/common/UserAgent.kt @@ -1,5 +1,6 @@ package org.grakovne.lissen.channel.common import android.os.Build +import org.grakovne.lissen.BuildConfig -val USER_AGENT = "Lissen (Android ${Build.VERSION.RELEASE}; ${Build.MODEL}" +val USER_AGENT = "Lissen/${BuildConfig.VERSION_NAME} (Linux; Android ${Build.VERSION.RELEASE}; ${Build.MODEL}) ExoPlayer/1.4.1" \ No newline at end of file diff --git a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt index 238e81b0..a556b845 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt @@ -1,6 +1,7 @@ package org.grakovne.lissen.playback.service import android.content.Intent +import android.os.Build import android.util.Log import androidx.annotation.OptIn import androidx.localbroadcastmanager.content.LocalBroadcastManager From 6aa29ae6ef73132d77844090db4ecec24c8e4325 Mon Sep 17 00:00:00 2001 From: grakovne Date: Thu, 7 Nov 2024 20:23:49 +0100 Subject: [PATCH 52/59] version set --- .../channel/audiobookshelf/api/ApiClientConfig.kt | 4 ++-- .../audiobookshelf/api/AudioBookshelfDataRepository.kt | 4 ++-- .../api/AudioBookshelfMediaRepository.kt | 2 +- .../audiobookshelf/api/RequestHeadersProvider.kt | 6 +++--- .../org/grakovne/lissen/channel/common/ApiClient.kt | 10 ++++------ .../grakovne/lissen/channel/common/BinaryApiClient.kt | 9 ++++----- .../org/grakovne/lissen/channel/common/UserAgent.kt | 3 +-- .../main/java/org/grakovne/lissen/domain/RequestUri.kt | 4 ++-- .../{ServerCustomHeader.kt => ServerRequestHeader.kt} | 4 ++-- .../persistence/preferences/LissenSharedPreferences.kt | 8 ++++---- .../java/org/grakovne/lissen/playback/MediaModule.kt | 2 -- .../lissen/playback/service/PlaybackService.kt | 1 - .../settings/advanced/CustomHeaderComposable.kt | 8 ++++---- .../settings/advanced/CustomHeadersSettingsScreen.kt | 8 ++++---- .../org/grakovne/lissen/viewmodel/SettingsViewModel.kt | 8 ++++---- 15 files changed, 37 insertions(+), 44 deletions(-) rename app/src/main/java/org/grakovne/lissen/domain/connection/{ServerCustomHeader.kt => ServerRequestHeader.kt} (85%) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/ApiClientConfig.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/ApiClientConfig.kt index c1f3baf8..6b6ff597 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/ApiClientConfig.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/ApiClientConfig.kt @@ -1,9 +1,9 @@ package org.grakovne.lissen.channel.audiobookshelf.api -import org.grakovne.lissen.domain.connection.ServerCustomHeader +import org.grakovne.lissen.domain.connection.ServerRequestHeader data class ApiClientConfig( val host: String?, val token: String?, - val customHeaders: List? + val customHeaders: List? ) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt index a0232a19..cc8ce4a3 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfDataRepository.kt @@ -111,7 +111,7 @@ class AudioBookshelfDataRepository @Inject constructor( try { val apiClient = ApiClient( host = host, - customHeaders = requestHeadersProvider.fetchRequestHeaders() + requestHeaders = requestHeadersProvider.fetchRequestHeaders() ) apiService = apiClient.retrofit.create(AudiobookshelfApiClient::class.java) @@ -202,7 +202,7 @@ class AudioBookshelfDataRepository @Inject constructor( ): ApiClient = ApiClient( host = host, token = token, - customHeaders = requestHeadersProvider.fetchRequestHeaders() + requestHeaders = requestHeadersProvider.fetchRequestHeaders() ) companion object { diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt index d019d991..85fdf110 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/AudioBookshelfMediaRepository.kt @@ -93,6 +93,6 @@ class AudioBookshelfMediaRepository @Inject constructor( ): BinaryApiClient = BinaryApiClient( host = host, token = token, - customHeaders = requestHeadersProvider.fetchRequestHeaders() + requestHeaders = requestHeadersProvider.fetchRequestHeaders() ) } diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/RequestHeadersProvider.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/RequestHeadersProvider.kt index bb2a43ad..6443dd54 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/RequestHeadersProvider.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/RequestHeadersProvider.kt @@ -1,7 +1,7 @@ package org.grakovne.lissen.channel.audiobookshelf.api import org.grakovne.lissen.channel.common.USER_AGENT -import org.grakovne.lissen.domain.connection.ServerCustomHeader +import org.grakovne.lissen.domain.connection.ServerRequestHeader import org.grakovne.lissen.persistence.preferences.LissenSharedPreferences import javax.inject.Inject import javax.inject.Singleton @@ -11,12 +11,12 @@ class RequestHeadersProvider @Inject constructor( private val preferences: LissenSharedPreferences ) { - fun fetchRequestHeaders(): List { + fun fetchRequestHeaders(): List { val usersHeaders = preferences .getCustomHeaders() ?: emptyList() - val userAgent = ServerCustomHeader("User-Agent", USER_AGENT) + val userAgent = ServerRequestHeader("User-Agent", USER_AGENT) return usersHeaders + userAgent } } diff --git a/app/src/main/java/org/grakovne/lissen/channel/common/ApiClient.kt b/app/src/main/java/org/grakovne/lissen/channel/common/ApiClient.kt index 358a68c5..69728c99 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/common/ApiClient.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/common/ApiClient.kt @@ -4,15 +4,15 @@ import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.logging.HttpLoggingInterceptor -import org.grakovne.lissen.domain.connection.ServerCustomHeader -import org.grakovne.lissen.domain.connection.ServerCustomHeader.Companion.clean +import org.grakovne.lissen.domain.connection.ServerRequestHeader +import org.grakovne.lissen.domain.connection.ServerRequestHeader.Companion.clean import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit class ApiClient( host: String, - customHeaders: List?, + requestHeaders: List?, token: String? = null ) { @@ -30,9 +30,7 @@ class ApiClient( requestBuilder.header("Authorization", "Bearer $token") } - requestBuilder.header("User-Agent", USER_AGENT) - - customHeaders + requestHeaders ?.filter { it.name.isNotEmpty() } ?.filter { it.value.isNotEmpty() } ?.forEach { requestBuilder.header(it.name.clean(), it.value.clean()) } diff --git a/app/src/main/java/org/grakovne/lissen/channel/common/BinaryApiClient.kt b/app/src/main/java/org/grakovne/lissen/channel/common/BinaryApiClient.kt index 339a913e..01e09b97 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/common/BinaryApiClient.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/common/BinaryApiClient.kt @@ -3,14 +3,14 @@ package org.grakovne.lissen.channel.common import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor -import org.grakovne.lissen.domain.connection.ServerCustomHeader -import org.grakovne.lissen.domain.connection.ServerCustomHeader.Companion.clean +import org.grakovne.lissen.domain.connection.ServerRequestHeader +import org.grakovne.lissen.domain.connection.ServerRequestHeader.Companion.clean import retrofit2.Retrofit import java.util.concurrent.TimeUnit class BinaryApiClient( host: String, - customHeaders: List?, + requestHeaders: List?, token: String ) { @@ -25,9 +25,8 @@ class BinaryApiClient( .request() .newBuilder() .header("Authorization", "Bearer $token") - .header("User-Agent", USER_AGENT) - customHeaders + requestHeaders ?.filter { it.name.isNotEmpty() } ?.filter { it.value.isNotEmpty() } ?.forEach { request.header(it.name.clean(), it.value.clean()) } diff --git a/app/src/main/java/org/grakovne/lissen/channel/common/UserAgent.kt b/app/src/main/java/org/grakovne/lissen/channel/common/UserAgent.kt index 798a01a6..1056e674 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/common/UserAgent.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/common/UserAgent.kt @@ -1,6 +1,5 @@ package org.grakovne.lissen.channel.common import android.os.Build -import org.grakovne.lissen.BuildConfig -val USER_AGENT = "Lissen/${BuildConfig.VERSION_NAME} (Linux; Android ${Build.VERSION.RELEASE}; ${Build.MODEL}) ExoPlayer/1.4.1" +val USER_AGENT = "Mozilla/5.0 (Linux; Android ${Build.VERSION.RELEASE}; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.106 Mobile Safari/537.36" diff --git a/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt b/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt index 65a3c1f2..aa65a27b 100644 --- a/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt +++ b/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt @@ -1,9 +1,9 @@ package org.grakovne.lissen.domain import android.net.Uri -import org.grakovne.lissen.domain.connection.ServerCustomHeader +import org.grakovne.lissen.domain.connection.ServerRequestHeader data class RequestUri( val uri: Uri, - val headers: List = emptyList() + val headers: List = emptyList() ) diff --git a/app/src/main/java/org/grakovne/lissen/domain/connection/ServerCustomHeader.kt b/app/src/main/java/org/grakovne/lissen/domain/connection/ServerRequestHeader.kt similarity index 85% rename from app/src/main/java/org/grakovne/lissen/domain/connection/ServerCustomHeader.kt rename to app/src/main/java/org/grakovne/lissen/domain/connection/ServerRequestHeader.kt index 9974401e..26104ff2 100644 --- a/app/src/main/java/org/grakovne/lissen/domain/connection/ServerCustomHeader.kt +++ b/app/src/main/java/org/grakovne/lissen/domain/connection/ServerRequestHeader.kt @@ -2,14 +2,14 @@ package org.grakovne.lissen.domain.connection import java.util.UUID -data class ServerCustomHeader( +data class ServerRequestHeader( val name: String, val value: String, val id: UUID = UUID.randomUUID() ) { companion object { - fun empty() = ServerCustomHeader("", "") + fun empty() = ServerRequestHeader("", "") fun String.clean(): String { var sanitized = this.replace(Regex("[\\r\\n]"), "") diff --git a/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt b/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt index c84231b8..8ff4a88c 100644 --- a/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt +++ b/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt @@ -15,7 +15,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import org.grakovne.lissen.channel.common.ChannelCode import org.grakovne.lissen.common.ColorScheme import org.grakovne.lissen.domain.Library -import org.grakovne.lissen.domain.connection.ServerCustomHeader +import org.grakovne.lissen.domain.connection.ServerRequestHeader import java.security.KeyStore import java.util.UUID import javax.crypto.Cipher @@ -151,7 +151,7 @@ class LissenSharedPreferences @Inject constructor(@ApplicationContext context: C return decrypt(encrypted) } - fun saveCustomHeaders(headers: List) { + fun saveCustomHeaders(headers: List) { val editor = sharedPreferences.edit() val json = gson.toJson(headers) @@ -159,9 +159,9 @@ class LissenSharedPreferences @Inject constructor(@ApplicationContext context: C editor.apply() } - fun getCustomHeaders(): List? { + fun getCustomHeaders(): List? { val json = sharedPreferences.getString(KEY_CUSTOM_HEADERS, null) - val type = object : TypeToken>() {}.type + val type = object : TypeToken>() {}.type return when (json == null) { true -> null diff --git a/app/src/main/java/org/grakovne/lissen/playback/MediaModule.kt b/app/src/main/java/org/grakovne/lissen/playback/MediaModule.kt index e03bafe4..ddc082bf 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/MediaModule.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/MediaModule.kt @@ -7,9 +7,7 @@ import androidx.annotation.OptIn import androidx.media3.common.AudioAttributes import androidx.media3.common.C import androidx.media3.common.util.UnstableApi -import androidx.media3.exoplayer.DefaultLoadControl import androidx.media3.exoplayer.ExoPlayer -import androidx.media3.exoplayer.LoadControl import androidx.media3.session.MediaSession import dagger.Module import dagger.Provides diff --git a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt index a556b845..238e81b0 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt @@ -1,7 +1,6 @@ package org.grakovne.lissen.playback.service import android.content.Intent -import android.os.Build import android.util.Log import androidx.annotation.OptIn import androidx.localbroadcastmanager.content.LocalBroadcastManager diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt index ded4bc9a..f82cac83 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt @@ -21,13 +21,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import org.grakovne.lissen.R -import org.grakovne.lissen.domain.connection.ServerCustomHeader +import org.grakovne.lissen.domain.connection.ServerRequestHeader @Composable fun CustomHeaderComposable( - header: ServerCustomHeader, - onChanged: (ServerCustomHeader) -> Unit, - onDelete: (ServerCustomHeader) -> Unit + header: ServerRequestHeader, + onChanged: (ServerRequestHeader) -> Unit, + onDelete: (ServerRequestHeader) -> Unit ) { Card( shape = RoundedCornerShape(12.dp), diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 549b99d9..5a36822b 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -35,7 +35,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import kotlinx.coroutines.launch import org.grakovne.lissen.R -import org.grakovne.lissen.domain.connection.ServerCustomHeader +import org.grakovne.lissen.domain.connection.ServerRequestHeader import org.grakovne.lissen.viewmodel.SettingsViewModel import kotlin.math.max @@ -91,7 +91,7 @@ fun CustomHeadersSettingsScreen( horizontalAlignment = Alignment.CenterHorizontally ) { val customHeaders = when (headers.value.isEmpty()) { - true -> listOf(ServerCustomHeader.empty()) + true -> listOf(ServerRequestHeader.empty()) false -> headers.value } @@ -109,7 +109,7 @@ fun CustomHeadersSettingsScreen( updatedList.remove(pair) if (updatedList.isEmpty()) { - updatedList.add(ServerCustomHeader.empty()) + updatedList.add(ServerRequestHeader.empty()) } settingsViewModel.updateCustomHeaders(updatedList) @@ -133,7 +133,7 @@ fun CustomHeadersSettingsScreen( shape = CircleShape, onClick = { val updatedList = headers.value.toMutableList() - updatedList.add(ServerCustomHeader.empty()) + updatedList.add(ServerRequestHeader.empty()) settingsViewModel.updateCustomHeaders(updatedList) coroutineScope.launch { diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt index c349c3ef..d5fa49ff 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt @@ -9,7 +9,7 @@ import org.grakovne.lissen.channel.common.ApiResult import org.grakovne.lissen.common.ColorScheme import org.grakovne.lissen.content.LissenMediaProvider import org.grakovne.lissen.domain.Library -import org.grakovne.lissen.domain.connection.ServerCustomHeader +import org.grakovne.lissen.domain.connection.ServerRequestHeader import org.grakovne.lissen.persistence.preferences.LissenSharedPreferences import javax.inject.Inject @@ -79,7 +79,7 @@ class SettingsViewModel @Inject constructor( preferences.saveColorScheme(colorScheme) } - fun updateCustomHeaders(headers: List) { + fun updateCustomHeaders(headers: List) { _customHeaders.value = headers } @@ -92,7 +92,7 @@ class SettingsViewModel @Inject constructor( preferences.saveCustomHeaders(_customHeaders.value ?: emptyList()) } - private fun fetchCustomHeaders(): List = preferences + private fun fetchCustomHeaders(): List = preferences .getCustomHeaders() - ?: listOf(ServerCustomHeader.empty()) + ?: listOf(ServerRequestHeader.empty()) } From 19c2aec06ba74688e48c244415a91cbbcd7158d7 Mon Sep 17 00:00:00 2001 From: grakovne Date: Thu, 7 Nov 2024 21:23:31 +0100 Subject: [PATCH 53/59] wip --- .../audiobookshelf/AudiobookshelfChannel.kt | 30 +++++--------- .../lissen/channel/common/MediaChannel.kt | 4 +- .../lissen/content/LissenMediaProvider.kt | 4 +- .../content/cache/BookCachingService.kt | 10 +++-- .../content/cache/LocalCacheRepository.kt | 5 +-- .../org/grakovne/lissen/domain/RequestUri.kt | 9 ----- .../playback/service/PlaybackService.kt | 39 ++++++++++++------- 7 files changed, 46 insertions(+), 55 deletions(-) delete mode 100644 app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt index 1ba41b43..c091d29a 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt @@ -8,7 +8,6 @@ import org.grakovne.lissen.BuildConfig import org.grakovne.lissen.channel.audiobookshelf.api.AudioBookshelfDataRepository import org.grakovne.lissen.channel.audiobookshelf.api.AudioBookshelfMediaRepository import org.grakovne.lissen.channel.audiobookshelf.api.AudioBookshelfSyncService -import org.grakovne.lissen.channel.audiobookshelf.api.RequestHeadersProvider import org.grakovne.lissen.channel.audiobookshelf.converter.LibraryItemIdResponseConverter import org.grakovne.lissen.channel.audiobookshelf.converter.LibraryItemResponseConverter import org.grakovne.lissen.channel.audiobookshelf.converter.LibraryResponseConverter @@ -29,7 +28,6 @@ import org.grakovne.lissen.domain.PagedItems import org.grakovne.lissen.domain.PlaybackProgress import org.grakovne.lissen.domain.PlaybackSession import org.grakovne.lissen.domain.RecentBook -import org.grakovne.lissen.domain.RequestUri import org.grakovne.lissen.domain.UserAccount import org.grakovne.lissen.persistence.preferences.LissenSharedPreferences import java.io.InputStream @@ -47,8 +45,7 @@ class AudiobookshelfChannel @Inject constructor( private val sessionResponseConverter: PlaybackSessionResponseConverter, private val librarySearchItemsConverter: LibrarySearchItemsConverter, private val preferences: LissenSharedPreferences, - private val syncService: AudioBookshelfSyncService, - private val requestHeadersProvider: RequestHeadersProvider + private val syncService: AudioBookshelfSyncService ) : MediaChannel { override fun getChannelCode() = ChannelCode.AUDIOBOOKSHELF @@ -56,22 +53,15 @@ class AudiobookshelfChannel @Inject constructor( override fun provideFileUri( libraryItemId: String, fileId: String - ): RequestUri { - val uri = Uri.parse(preferences.getHost()) - .buildUpon() - .appendPath("api") - .appendPath("items") - .appendPath(libraryItemId) - .appendPath("file") - .appendPath(fileId) - .appendQueryParameter("token", preferences.getToken()) - .build() - - return RequestUri( - uri = uri, - headers = requestHeadersProvider.fetchRequestHeaders() - ) - } + ): Uri = Uri.parse(preferences.getHost()) + .buildUpon() + .appendPath("api") + .appendPath("items") + .appendPath(libraryItemId) + .appendPath("file") + .appendPath(fileId) + .appendQueryParameter("token", preferences.getToken()) + .build() override suspend fun syncProgress( sessionId: String, diff --git a/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt b/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt index b8424978..d794c9b9 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/common/MediaChannel.kt @@ -1,5 +1,6 @@ package org.grakovne.lissen.channel.common +import android.net.Uri import org.grakovne.lissen.domain.Book import org.grakovne.lissen.domain.DetailedBook import org.grakovne.lissen.domain.Library @@ -7,7 +8,6 @@ import org.grakovne.lissen.domain.PagedItems import org.grakovne.lissen.domain.PlaybackProgress import org.grakovne.lissen.domain.PlaybackSession import org.grakovne.lissen.domain.RecentBook -import org.grakovne.lissen.domain.RequestUri import org.grakovne.lissen.domain.UserAccount import java.io.InputStream @@ -18,7 +18,7 @@ interface MediaChannel { fun provideFileUri( libraryItemId: String, fileId: String - ): RequestUri + ): Uri suspend fun syncProgress( sessionId: String, diff --git a/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt b/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt index ccdc3592..cbc12086 100644 --- a/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt +++ b/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt @@ -1,5 +1,6 @@ package org.grakovne.lissen.content +import android.net.Uri import android.util.Log import org.grakovne.lissen.channel.common.ApiError import org.grakovne.lissen.channel.common.ApiResult @@ -15,7 +16,6 @@ import org.grakovne.lissen.domain.PagedItems import org.grakovne.lissen.domain.PlaybackProgress import org.grakovne.lissen.domain.PlaybackSession import org.grakovne.lissen.domain.RecentBook -import org.grakovne.lissen.domain.RequestUri import org.grakovne.lissen.domain.UserAccount import org.grakovne.lissen.persistence.preferences.LissenSharedPreferences import java.io.InputStream @@ -33,7 +33,7 @@ class LissenMediaProvider @Inject constructor( fun provideFileUri( libraryItemId: String, chapterId: String - ): ApiResult { + ): ApiResult { Log.d(TAG, "Fetching File $libraryItemId and $chapterId URI") return when (cacheConfiguration.localCacheUsing()) { diff --git a/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt b/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt index 685795be..cbddb22e 100644 --- a/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt +++ b/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flow import kotlinx.coroutines.withContext +import org.grakovne.lissen.channel.audiobookshelf.api.RequestHeadersProvider import org.grakovne.lissen.channel.common.MediaChannel import org.grakovne.lissen.content.cache.api.CachedBookRepository import org.grakovne.lissen.content.cache.api.CachedLibraryRepository @@ -30,7 +31,8 @@ class BookCachingService @Inject constructor( @ApplicationContext private val context: Context, private val bookRepository: CachedBookRepository, private val libraryRepository: CachedLibraryRepository, - private val properties: CacheBookStorageProperties + private val properties: CacheBookStorageProperties, + private val requestHeadersProvider: RequestHeadersProvider ) { fun cacheBook( @@ -89,15 +91,15 @@ class BookCachingService @Inject constructor( .files .map { file -> val serverRequest = channel.provideFileUri(book.id, file.id) - val downloadRequest = Request(serverRequest.uri) + val downloadRequest = Request(serverRequest) .setTitle(file.name) .setNotificationVisibility(VISIBILITY_VISIBLE) .setDestinationUri(properties.provideMediaCachePatch(book.id, file.id).toUri()) .setAllowedOverMetered(true) .setAllowedOverRoaming(true) - serverRequest - .headers + requestHeadersProvider + .fetchRequestHeaders() .forEach { downloadRequest.addRequestHeader(it.name, it.value) } downloadRequest.let { downloadManager.enqueue(it) } diff --git a/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt b/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt index 618f8d0f..0cbffdbe 100644 --- a/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt +++ b/app/src/main/java/org/grakovne/lissen/content/cache/LocalCacheRepository.kt @@ -1,5 +1,6 @@ package org.grakovne.lissen.content.cache +import android.net.Uri import androidx.core.net.toFile import org.grakovne.lissen.channel.common.ApiError import org.grakovne.lissen.channel.common.ApiResult @@ -11,7 +12,6 @@ import org.grakovne.lissen.domain.PagedItems import org.grakovne.lissen.domain.PlaybackProgress import org.grakovne.lissen.domain.PlaybackSession import org.grakovne.lissen.domain.RecentBook -import org.grakovne.lissen.domain.RequestUri import java.io.InputStream import javax.inject.Inject import javax.inject.Singleton @@ -23,11 +23,10 @@ class LocalCacheRepository @Inject constructor( private val properties: CacheBookStorageProperties ) { - fun provideFileUri(libraryItemId: String, fileId: String): RequestUri? = + fun provideFileUri(libraryItemId: String, fileId: String): Uri? = cachedBookRepository .provideFileUri(libraryItemId, fileId) .takeIf { it.toFile().exists() } - ?.let { RequestUri(uri = it) } /** * For the local cache we avoiding to create intermediary entity like Session and using BookId diff --git a/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt b/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt deleted file mode 100644 index aa65a27b..00000000 --- a/app/src/main/java/org/grakovne/lissen/domain/RequestUri.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.grakovne.lissen.domain - -import android.net.Uri -import org.grakovne.lissen.domain.connection.ServerRequestHeader - -data class RequestUri( - val uri: Uri, - val headers: List = emptyList() -) diff --git a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt index 238e81b0..cde354cd 100644 --- a/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt +++ b/app/src/main/java/org/grakovne/lissen/playback/service/PlaybackService.kt @@ -22,6 +22,7 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.grakovne.lissen.channel.audiobookshelf.api.RequestHeadersProvider import org.grakovne.lissen.content.LissenMediaProvider import org.grakovne.lissen.domain.BookFile import org.grakovne.lissen.domain.DetailedBook @@ -50,6 +51,9 @@ class PlaybackService : MediaSessionService() { @Inject lateinit var channelProvider: LissenMediaProvider + @Inject + lateinit var requestHeadersProvider: RequestHeadersProvider + private val playerServiceScope = MainScope() @Suppress("DEPRECATION") @@ -134,6 +138,8 @@ class PlaybackService : MediaSessionService() { onFailure = { null } ) + val sourceFactory = buildDataSourceFactory() + val playingQueue = book .files .mapNotNull { file -> @@ -141,19 +147,6 @@ class PlaybackService : MediaSessionService() { .provideFileUri(book.id, file.id) .fold( onSuccess = { request -> - val httpDataSourceFactory = DefaultHttpDataSource - .Factory() - .setDefaultRequestProperties( - request - .headers - .associate { it.name to it.value } - ) - - val dataSourceFactory = DefaultDataSource.Factory( - baseContext, - httpDataSourceFactory - ) - val mediaData = MediaMetadata.Builder() .setTitle(file.name) .setArtist(book.title) @@ -162,13 +155,13 @@ class PlaybackService : MediaSessionService() { val mediaItem = MediaItem.Builder() .setMediaId(file.id) - .setUri(request.uri) + .setUri(request) .setTag(book) .setMediaMetadata(mediaData.build()) .build() ProgressiveMediaSource - .Factory(dataSourceFactory) + .Factory(sourceFactory) .createMediaSource(mediaItem) }, onFailure = { null } @@ -235,6 +228,22 @@ class PlaybackService : MediaSessionService() { progress: MediaProgress? ) = seek(chapters, progress?.currentTime) + @OptIn(UnstableApi::class) + private fun buildDataSourceFactory(): DefaultDataSource.Factory { + val requestHeaders = requestHeadersProvider + .fetchRequestHeaders() + .associate { it.name to it.value } + + val networkDatasourceFactory = DefaultHttpDataSource + .Factory() + .setDefaultRequestProperties(requestHeaders) + + return DefaultDataSource.Factory( + baseContext, + networkDatasourceFactory + ) + } + companion object { const val ACTION_PLAY = "org.grakovne.lissen.player.service.PLAY" From 5c079e5c9ecbfff30068a60319f01e1ff2a871b9 Mon Sep 17 00:00:00 2001 From: grakovne Date: Thu, 7 Nov 2024 21:36:39 +0100 Subject: [PATCH 54/59] wip --- .../java/org/grakovne/lissen/content/LissenMediaProvider.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt b/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt index cbc12086..6494c454 100644 --- a/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt +++ b/app/src/main/java/org/grakovne/lissen/content/LissenMediaProvider.kt @@ -40,9 +40,7 @@ class LissenMediaProvider @Inject constructor( true -> localCacheRepository .provideFileUri(libraryItemId, chapterId) - ?.let { - ApiResult.Success(it) - } + ?.let { ApiResult.Success(it) } ?: ApiResult.Error(ApiError.InternalError) false -> From 3b068beeeac3a87e591f7f9db97b60bdb38d7dd4 Mon Sep 17 00:00:00 2001 From: grakovne Date: Thu, 7 Nov 2024 21:39:45 +0100 Subject: [PATCH 55/59] wip --- .../settings/advanced/CustomHeadersSettingsScreen.kt | 1 - .../grakovne/lissen/viewmodel/SettingsViewModel.kt | 11 ++++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt index 5a36822b..72dae5f1 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeadersSettingsScreen.kt @@ -66,7 +66,6 @@ fun CustomHeadersSettingsScreen( navigationIcon = { IconButton(onClick = { onBack() - settingsViewModel.saveCustomHeaders() }) { Icon( imageVector = Icons.AutoMirrored.Outlined.ArrowBack, diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt index d5fa49ff..bfd8ccae 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt @@ -81,15 +81,12 @@ class SettingsViewModel @Inject constructor( fun updateCustomHeaders(headers: List) { _customHeaders.value = headers - } - fun saveCustomHeaders() { - _customHeaders.value = _customHeaders - .value - ?.filterNot { it.name.isEmpty() } - ?.filterNot { it.value.isEmpty() } + val meaningfulHeaders = headers + .filterNot { it.name.isEmpty() } + .filterNot { it.value.isEmpty() } - preferences.saveCustomHeaders(_customHeaders.value ?: emptyList()) + preferences.saveCustomHeaders(meaningfulHeaders) } private fun fetchCustomHeaders(): List = preferences From b7b3201498b89be55bc3cd83f92ee82b4efb8c3f Mon Sep 17 00:00:00 2001 From: grakovne Date: Thu, 7 Nov 2024 21:41:18 +0100 Subject: [PATCH 56/59] wip --- .../channel/audiobookshelf/api/RequestHeadersProvider.kt | 4 +--- .../persistence/preferences/LissenSharedPreferences.kt | 4 ++-- .../org/grakovne/lissen/viewmodel/SettingsViewModel.kt | 7 ++----- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/RequestHeadersProvider.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/RequestHeadersProvider.kt index 6443dd54..d26fea6f 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/RequestHeadersProvider.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/api/RequestHeadersProvider.kt @@ -12,9 +12,7 @@ class RequestHeadersProvider @Inject constructor( ) { fun fetchRequestHeaders(): List { - val usersHeaders = preferences - .getCustomHeaders() - ?: emptyList() + val usersHeaders = preferences.getCustomHeaders() val userAgent = ServerRequestHeader("User-Agent", USER_AGENT) return usersHeaders + userAgent diff --git a/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt b/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt index 8ff4a88c..e0df51b8 100644 --- a/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt +++ b/app/src/main/java/org/grakovne/lissen/persistence/preferences/LissenSharedPreferences.kt @@ -159,12 +159,12 @@ class LissenSharedPreferences @Inject constructor(@ApplicationContext context: C editor.apply() } - fun getCustomHeaders(): List? { + fun getCustomHeaders(): List { val json = sharedPreferences.getString(KEY_CUSTOM_HEADERS, null) val type = object : TypeToken>() {}.type return when (json == null) { - true -> null + true -> emptyList() false -> gson.fromJson(json, type) } } diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt index bfd8ccae..8617c535 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt @@ -34,7 +34,7 @@ class SettingsViewModel @Inject constructor( private val _preferredColorScheme = MutableLiveData(preferences.getColorScheme()) val preferredColorScheme = _preferredColorScheme - private val _customHeaders = MutableLiveData(fetchCustomHeaders()) + private val _customHeaders = MutableLiveData(preferences.getCustomHeaders()) val customHeaders = _customHeaders fun logout() { @@ -83,13 +83,10 @@ class SettingsViewModel @Inject constructor( _customHeaders.value = headers val meaningfulHeaders = headers + .distinctBy { it.name } .filterNot { it.name.isEmpty() } .filterNot { it.value.isEmpty() } preferences.saveCustomHeaders(meaningfulHeaders) } - - private fun fetchCustomHeaders(): List = preferences - .getCustomHeaders() - ?: listOf(ServerRequestHeader.empty()) } From fe9e5c6b14021e1f433f9051885e6f008f5ffd55 Mon Sep 17 00:00:00 2001 From: grakovne Date: Thu, 7 Nov 2024 21:42:01 +0100 Subject: [PATCH 57/59] wip --- .../lissen/channel/audiobookshelf/AudiobookshelfChannel.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt index c091d29a..2adced8c 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/audiobookshelf/AudiobookshelfChannel.kt @@ -177,9 +177,7 @@ class AudiobookshelfChannel @Inject constructor( onFailure = { Success(libraryItemIdResponseConverter.apply(item, null)) } ) }, - onFailure = { - ApiResult.Error(it.code) - } + onFailure = { ApiResult.Error(it.code) } ) } From 73318ad0c5f43166b65d05bf10e2e41766bd7ecb Mon Sep 17 00:00:00 2001 From: grakovne Date: Thu, 7 Nov 2024 21:48:33 +0100 Subject: [PATCH 58/59] wip --- .../java/org/grakovne/lissen/channel/common/ApiClient.kt | 3 +-- .../grakovne/lissen/channel/common/BinaryApiClient.kt | 3 +-- .../lissen/domain/connection/ServerRequestHeader.kt | 9 ++++++++- .../org/grakovne/lissen/viewmodel/SettingsViewModel.kt | 2 ++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/grakovne/lissen/channel/common/ApiClient.kt b/app/src/main/java/org/grakovne/lissen/channel/common/ApiClient.kt index 69728c99..935e9d1a 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/common/ApiClient.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/common/ApiClient.kt @@ -5,7 +5,6 @@ import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.logging.HttpLoggingInterceptor import org.grakovne.lissen.domain.connection.ServerRequestHeader -import org.grakovne.lissen.domain.connection.ServerRequestHeader.Companion.clean import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit @@ -33,7 +32,7 @@ class ApiClient( requestHeaders ?.filter { it.name.isNotEmpty() } ?.filter { it.value.isNotEmpty() } - ?.forEach { requestBuilder.header(it.name.clean(), it.value.clean()) } + ?.forEach { requestBuilder.header(it.name, it.value) } val request: Request = requestBuilder.build() chain.proceed(request) diff --git a/app/src/main/java/org/grakovne/lissen/channel/common/BinaryApiClient.kt b/app/src/main/java/org/grakovne/lissen/channel/common/BinaryApiClient.kt index 01e09b97..27cc8ffb 100644 --- a/app/src/main/java/org/grakovne/lissen/channel/common/BinaryApiClient.kt +++ b/app/src/main/java/org/grakovne/lissen/channel/common/BinaryApiClient.kt @@ -4,7 +4,6 @@ import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import org.grakovne.lissen.domain.connection.ServerRequestHeader -import org.grakovne.lissen.domain.connection.ServerRequestHeader.Companion.clean import retrofit2.Retrofit import java.util.concurrent.TimeUnit @@ -29,7 +28,7 @@ class BinaryApiClient( requestHeaders ?.filter { it.name.isNotEmpty() } ?.filter { it.value.isNotEmpty() } - ?.forEach { request.header(it.name.clean(), it.value.clean()) } + ?.forEach { request.header(it.name, it.value) } chain.proceed(request.build()) } diff --git a/app/src/main/java/org/grakovne/lissen/domain/connection/ServerRequestHeader.kt b/app/src/main/java/org/grakovne/lissen/domain/connection/ServerRequestHeader.kt index 26104ff2..589b60ef 100644 --- a/app/src/main/java/org/grakovne/lissen/domain/connection/ServerRequestHeader.kt +++ b/app/src/main/java/org/grakovne/lissen/domain/connection/ServerRequestHeader.kt @@ -11,7 +11,14 @@ data class ServerRequestHeader( companion object { fun empty() = ServerRequestHeader("", "") - fun String.clean(): String { + fun ServerRequestHeader.clean(): ServerRequestHeader { + val name = this.name.clean() + val value = this.value.clean() + + return this.copy(name = name, value = value) + } + + private fun String.clean(): String { var sanitized = this.replace(Regex("[\\r\\n]"), "") sanitized = sanitized.replace(Regex("[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]"), "") sanitized = sanitized.trim() diff --git a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt index 8617c535..f57a3f01 100644 --- a/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/org/grakovne/lissen/viewmodel/SettingsViewModel.kt @@ -10,6 +10,7 @@ import org.grakovne.lissen.common.ColorScheme import org.grakovne.lissen.content.LissenMediaProvider import org.grakovne.lissen.domain.Library import org.grakovne.lissen.domain.connection.ServerRequestHeader +import org.grakovne.lissen.domain.connection.ServerRequestHeader.Companion.clean import org.grakovne.lissen.persistence.preferences.LissenSharedPreferences import javax.inject.Inject @@ -83,6 +84,7 @@ class SettingsViewModel @Inject constructor( _customHeaders.value = headers val meaningfulHeaders = headers + .map { it.clean() } .distinctBy { it.name } .filterNot { it.name.isEmpty() } .filterNot { it.value.isEmpty() } From 7015187af6233f9dd2c04907b5c6a326d3e89430 Mon Sep 17 00:00:00 2001 From: grakovne Date: Thu, 7 Nov 2024 22:02:13 +0100 Subject: [PATCH 59/59] version set --- app/build.gradle.kts | 4 ++-- .../org/grakovne/lissen/content/cache/BookCachingService.kt | 5 +++-- .../ui/screens/settings/advanced/CustomHeaderComposable.kt | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 49052dfe..a296fda3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -25,8 +25,8 @@ android { applicationId = "org.grakovne.lissen" minSdk = 28 targetSdk = 35 - versionCode = 24 - versionName = "1.0.23" + versionCode = 25 + versionName = "1.0.24" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt b/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt index cbddb22e..6d19d296 100644 --- a/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt +++ b/app/src/main/java/org/grakovne/lissen/content/cache/BookCachingService.kt @@ -90,8 +90,9 @@ class BookCachingService @Inject constructor( val downloads = book .files .map { file -> - val serverRequest = channel.provideFileUri(book.id, file.id) - val downloadRequest = Request(serverRequest) + val uri = channel.provideFileUri(book.id, file.id) + + val downloadRequest = Request(uri) .setTitle(file.name) .setNotificationVisibility(VISIBILITY_VISIBLE) .setDestinationUri(properties.provideMediaCachePatch(book.id, file.id).toUri()) diff --git a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt index f82cac83..15d83e27 100644 --- a/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt +++ b/app/src/main/java/org/grakovne/lissen/ui/screens/settings/advanced/CustomHeaderComposable.kt @@ -70,7 +70,7 @@ fun CustomHeaderComposable( imageVector = Icons.Default.DeleteOutline, contentDescription = null, tint = colorScheme.error, - modifier = Modifier.size(28.dp) + modifier = Modifier.size(32.dp) ) } }