diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
index 9411831..b82eb7a 100644
--- a/.idea/deploymentTargetDropDown.xml
+++ b/.idea/deploymentTargetDropDown.xml
@@ -7,11 +7,11 @@
-
+
-
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index a2b26f0..82ca760 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -13,9 +13,8 @@ android {
applicationId "com.lighttigerxiv.simple.mp"
minSdk 26
targetSdk 33
- versionCode 21
- versionName "BETA-1.7.1"
-
+ versionCode 22
+ versionName "BETA-1.8.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
@@ -102,4 +101,8 @@ dependencies {
//Catppuccin Colors
implementation 'com.github.lighttigerxiv:catppuccin-kt:1.0.0'
+
+ //Android Auto
+ implementation 'androidx.car.app:app:1.2.0'
+
}
\ No newline at end of file
diff --git a/app/release/SimpleMP-BETA-1.7.1.apk b/app/release/SimpleMP-BETA-1.8.0.apk
similarity index 93%
rename from app/release/SimpleMP-BETA-1.7.1.apk
rename to app/release/SimpleMP-BETA-1.8.0.apk
index 22df897..0e558d0 100644
Binary files a/app/release/SimpleMP-BETA-1.7.1.apk and b/app/release/SimpleMP-BETA-1.8.0.apk differ
diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json
index be41e57..c3a28e5 100644
--- a/app/release/output-metadata.json
+++ b/app/release/output-metadata.json
@@ -11,8 +11,8 @@
"type": "SINGLE",
"filters": [],
"attributes": [],
- "versionCode": 21,
- "versionName": "BETA-1.7.1",
+ "versionCode": 22,
+ "versionName": "BETA-1.8.0",
"outputFile": "app-release.apk"
}
],
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/AndroidAutoDetector.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/AndroidAutoDetector.kt
new file mode 100644
index 0000000..4d7f4a6
--- /dev/null
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/AndroidAutoDetector.kt
@@ -0,0 +1,101 @@
+package com.lighttigerxiv.simple.mp.compose
+
+import android.annotation.SuppressLint
+import android.content.AsyncQueryHandler
+import android.content.BroadcastReceiver
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.database.Cursor
+import android.net.Uri
+import androidx.core.content.ContextCompat
+
+
+class AndroidAutoDetector(val context: Context) {
+
+ var onDisconnect: () -> Unit = {}
+
+ companion object {
+ const val TAG = "AutoConnectionDetector"
+
+ // columnName for provider to query on connection status
+ const val CAR_CONNECTION_STATE = "CarConnectionState"
+
+ // auto app on your phone will send broadcast with this action when connection state changes
+ const val ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED"
+
+ // phone is not connected to car
+ const val CONNECTION_TYPE_NOT_CONNECTED = 0
+
+ // phone is connected to Automotive OS
+ const val CONNECTION_TYPE_NATIVE = 1
+
+ // phone is connected to Android Auto
+ const val CONNECTION_TYPE_PROJECTION = 2
+
+ private const val QUERY_TOKEN = 42
+
+ private const val CAR_CONNECTION_AUTHORITY = "androidx.car.app.connection"
+
+ private val PROJECTION_HOST_URI = Uri.Builder().scheme("content").authority(CAR_CONNECTION_AUTHORITY).build()
+ }
+
+ private val carConnectionReceiver = CarConnectionBroadcastReceiver()
+ private val carConnectionQueryHandler = CarConnectionQueryHandler(context.contentResolver)
+
+
+ fun registerCarConnectionReceiver() {
+
+ ContextCompat.registerReceiver(context, carConnectionReceiver, IntentFilter(ACTION_CAR_CONNECTION_UPDATED), ContextCompat.RECEIVER_NOT_EXPORTED)
+ queryForState()
+ }
+
+ fun unRegisterCarConnectionReceiver() {
+ context.unregisterReceiver(carConnectionReceiver)
+ }
+
+ private fun queryForState() {
+ carConnectionQueryHandler.startQuery(
+ QUERY_TOKEN,
+ null,
+ PROJECTION_HOST_URI,
+ arrayOf(CAR_CONNECTION_STATE),
+ null,
+ null,
+ null
+ )
+ }
+
+ inner class CarConnectionBroadcastReceiver : BroadcastReceiver() {
+ // query for connection state every time the receiver receives the broadcast
+ override fun onReceive(context: Context?, intent: Intent?) {
+ queryForState()
+ }
+ }
+
+ internal class CarConnectionQueryHandler(resolver: ContentResolver?) : AsyncQueryHandler(resolver) {
+ // notify new queryed connection status when query complete
+ override fun onQueryComplete(token: Int, cookie: Any?, response: Cursor?) {
+ if (response == null) {
+
+ return
+ }
+ val carConnectionTypeColumn = response.getColumnIndex(CAR_CONNECTION_STATE)
+ if (carConnectionTypeColumn < 0) {
+
+ return
+ }
+ if (!response.moveToNext()) {
+
+ return
+ }
+ val connectionState = response.getInt(carConnectionTypeColumn)
+ if (connectionState == CONNECTION_TYPE_NOT_CONNECTED) {
+
+ } else {
+
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/activities/main/ActivityMain.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/activities/main/ActivityMain.kt
index b1c9735..0d16e90 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/activities/main/ActivityMain.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/activities/main/ActivityMain.kt
@@ -11,6 +11,7 @@ import android.provider.MediaStore
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
+import androidx.car.app.connection.CarConnection
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
@@ -55,6 +56,7 @@ import com.lighttigerxiv.simple.mp.compose.screens.main.floating_album.FloatingA
import com.lighttigerxiv.simple.mp.compose.screens.main.floating_artist.FloatingArtistScreen
import com.lighttigerxiv.simple.mp.compose.screens.main.floating_artist.FloatingArtistScreenVM
import com.lighttigerxiv.simple.mp.compose.screens.main.main.MainScreen
+import com.lighttigerxiv.simple.mp.compose.screens.main.player.PlayerScreenVM
import com.lighttigerxiv.simple.mp.compose.screens.main.playlists.PlaylistsScreenVM
import com.lighttigerxiv.simple.mp.compose.screens.main.playlists.playlist.PlaylistScreenVM
import com.lighttigerxiv.simple.mp.compose.screens.main.playlists.playlist.add_songs.AddSongsScreen
@@ -82,8 +84,6 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
-
-
if (!getSharedPreferences(packageName, MODE_PRIVATE).getBoolean("setupCompleted", false)) {
startActivity(
@@ -93,12 +93,39 @@ class MainActivity : ComponentActivity() {
finish()
}
+
createNotificationChannels()
val vm = ViewModelProvider(this)[MainVM::class.java]
val settingsVM = ViewModelProvider(this)[SettingsVM::class.java]
+ fun onAndroidAutoStateUpdate(connectionState: Int){
+
+ val carPlayerEnabled = settingsVM.carPlayerSetting.value
+ val showCarPlayer = vm.showCarPlayer.value
+
+ when(connectionState){
+ CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> {
+
+ if(carPlayerEnabled && !showCarPlayer){
+ vm.updateShowCarPlayer(false)
+ }
+ }
+ CarConnection.CONNECTION_TYPE_NATIVE ->{
+ if(carPlayerEnabled && !showCarPlayer){
+ vm.updateShowCarPlayer(true)
+ }
+ }
+ CarConnection.CONNECTION_TYPE_PROJECTION ->{
+ if(carPlayerEnabled && !showCarPlayer){
+ vm.updateShowCarPlayer(true)
+ }
+ }
+ }
+ }
+
+ CarConnection(application).type.observe(this, ::onAndroidAutoStateUpdate)
setContent {
@@ -200,6 +227,7 @@ class MainActivity : ComponentActivity() {
MainScreen(
mainVM = vm,
+ settingsVM = settingsVM,
activityContext = activityContext,
rootNavController = rootNavController,
onGetPlaylistImage = {
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/activities/main/MainVM.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/activities/main/MainVM.kt
index 7d60113..1e0cfa7 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/activities/main/MainVM.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/activities/main/MainVM.kt
@@ -14,6 +14,7 @@ import android.os.IBinder
import android.provider.MediaStore
import android.util.Log
import android.util.Size
+import androidx.car.app.connection.CarConnection
import androidx.compose.material.BottomSheetState
import androidx.compose.material.BottomSheetValue
import androidx.compose.material.ExperimentalMaterialApi
@@ -64,6 +65,12 @@ class MainVM(application: Application) : AndroidViewModel(application) {
private val _loadingSongs = MutableStateFlow(true)
val loadingSongs = _loadingSongs.asStateFlow()
+ private val _showCarPlayer = MutableStateFlow(false)
+ val showCarPlayer = _showCarPlayer.asStateFlow()
+ fun updateShowCarPlayer(newValue: Boolean){
+ _showCarPlayer.update { newValue }
+ }
+
private val cachedQueries = CacheQueries(getMongoRealm())
@@ -756,6 +763,7 @@ class MainVM(application: Application) : AndroidViewModel(application) {
val workManager = WorkManager.getInstance(application)
workManager.enqueueUniquePeriodicWork("SyncSongsRequest", ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, syncSongsRequest)
+
val serviceIntent = Intent(context, SimpleMPService::class.java)
context.bindService(serviceIntent, simpleMPConnection, Context.BIND_AUTO_CREATE)
}
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/data/variables/Constants.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/data/variables/Constants.kt
index 58fba10..5f82d46 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/data/variables/Constants.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/data/variables/Constants.kt
@@ -58,6 +58,8 @@ object Settings{
const val HOME_SORT = "HomeSort"
const val ARTISTS_SORT = "ArtistsSort"
const val ALBUMS_SORT = "AlbumsSort"
+ const val CAR_PLAYER = "CarPlayer"
+ const val KEEP_SCREEN_ON_IN_CAR_MODE = "KeepScreenOnInCarMode"
object Values{
object ColorScheme{
@@ -143,6 +145,8 @@ object Settings{
const val OLDEST = "oldest"
const val ASCENDENT = "ascendent"
const val DESCENDENT = "descendent"
+ const val ARTIST_ASCENDENT = "artistAscendent"
+ const val ARTIST_DESCENDENT = "artistDescendent"
}
}
}
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/data/workers/SyncSongsWorker.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/data/workers/SyncSongsWorker.kt
index 91c4d3b..7d713d0 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/data/workers/SyncSongsWorker.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/data/workers/SyncSongsWorker.kt
@@ -20,10 +20,18 @@ import kotlinx.coroutines.withContext
import java.io.File
class SyncSongsWorker(appContext: Context, params: WorkerParameters) : CoroutineWorker(appContext, params) {
+
+
@SuppressLint("Range")
override suspend fun doWork(): Result {
+ return withContext(Dispatchers.IO) {
+
+ val preferences = applicationContext.getSharedPreferences(applicationContext.packageName, Context.MODE_PRIVATE)
+ val indexingSongs = preferences.getBoolean("indexingSongs", false)
- return withContext(Dispatchers.IO){
+ if (indexingSongs) return@withContext Result.success()
+
+ preferences.edit().putBoolean("indexingSongs", true).apply()
val notificationManager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notificationBuilder = NotificationCompat.Builder(applicationContext, "Sync")
@@ -145,6 +153,8 @@ class SyncSongsWorker(appContext: Context, params: WorkerParameters) : Coroutine
notificationManager.cancel(3)
+ preferences.edit().putBoolean("indexingSongs", false).apply()
+
return@withContext Result.success()
}
}
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/about/AboutScreen.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/about/AboutScreen.kt
index b4dd39b..68f7f40 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/about/AboutScreen.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/about/AboutScreen.kt
@@ -92,17 +92,11 @@ fun AboutScreen(
.background(MaterialTheme.colorScheme.surfaceVariant)
.padding(MEDIUM_SPACING),
color = MaterialTheme.colorScheme.onSurface,
- text = "- Added a smooth animation when opening and closing player \n\n" +
- "- Added Latte theme \n\n" +
- "- Added Light theme and Dark Theme options \n\n" +
- "- Fixed Oled theme on home screen\n\n" +
- "- Fixed empty albums and artists showing up due to filter\n\n" +
- "- Fixed crash on Android 9 devices\n\n" +
- "- Fixed notification on Android 9\n\n" +
- "- Fixed songs that don't have artist id or album id showing up" +
- "- App now syncs songs in background when opening\n\n" +
- "- Added swipe to resync songs on home screen \n\n" +
- "- Other small UI Updates "
+ text = "- Added Experimental Car Player Mode - (It should open when connecting to android auto)\n\n" +
+ "- Added sort by artist option in Albums\n\n" +
+ "- Added German Translation (@Integraluminium)\n\n" +
+ "- Fixed local artist image not showing\n\n" +
+ "- Fixed other small bugs"
)
MediumVerticalSpacer()
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/albums/AlbumsScreen.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/albums/AlbumsScreen.kt
index bd19356..47b4f05 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/albums/AlbumsScreen.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/albums/AlbumsScreen.kt
@@ -55,6 +55,9 @@ fun AlbumsScreen(
val oldestAlbums = vm.oldestAlbums.collectAsState().value
val ascendentAlbums = vm.ascendentAlbums.collectAsState().value
val descendentAlbums = vm.descendentAlbums.collectAsState().value
+ val artistAscendentAlbums = vm.artistAscendentAlbums.collectAsState().value
+ val artistDescendentAlbums = vm.artistDescendentAlbums.collectAsState().value
+
val gridCellsCount = when (LocalConfiguration.current.orientation) {
Configuration.ORIENTATION_PORTRAIT -> 2
else -> 4
@@ -159,6 +162,36 @@ fun AlbumsScreen(
)
}
)
+
+ DropdownMenuItem(
+ text = {
+ Text(text = stringResource(id = R.string.SortByArtist))
+ },
+ onClick = {
+
+ val currentSort = context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE).getString(Settings.ALBUMS_SORT, Settings.Values.Sort.RECENT)
+ val filterAlgorithm = if (currentSort == Settings.Values.Sort.ARTIST_ASCENDENT) Settings.Values.Sort.ARTIST_DESCENDENT else Settings.Values.Sort.ARTIST_ASCENDENT
+ val filterAlbums = if(currentSort == Settings.Values.Sort.ARTIST_ASCENDENT) artistDescendentAlbums else artistAscendentAlbums
+
+ vm.updateSortType(filterAlgorithm)
+ vm.updateCurrentAlbums(filterAlbums)
+ scope.launch {
+ vm.updateMenuExpanded(false)
+ delay(200)
+ listState.scrollToItem(index = 0)
+ }
+ },
+ leadingIcon = {
+ Icon(
+ modifier = Modifier
+ .height(20.dp)
+ .width(20.dp),
+ painter = painterResource(id = R.drawable.person),
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.onSurface
+ )
+ }
+ )
}
}
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/albums/AlbumsScreenVM.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/albums/AlbumsScreenVM.kt
index ad354d5..6103eea 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/albums/AlbumsScreenVM.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/albums/AlbumsScreenVM.kt
@@ -44,7 +44,7 @@ class AlbumsScreenVM(application: Application) : AndroidViewModel(application) {
private val _currentAlbums = MutableStateFlow?>(null)
val currentAlbums = _currentAlbums.asStateFlow()
- fun updateCurrentAlbums(newValue: List?){
+ fun updateCurrentAlbums(newValue: List?) {
_currentAlbums.update { newValue }
}
@@ -60,6 +60,12 @@ class AlbumsScreenVM(application: Application) : AndroidViewModel(application) {
private val _descendentAlbums = MutableStateFlow?>(null)
val descendentAlbums = _descendentAlbums.asStateFlow()
+ private val _artistAscendentAlbums = MutableStateFlow?>(null)
+ val artistAscendentAlbums = _artistAscendentAlbums.asStateFlow()
+
+ private val _artistDescendentAlbums = MutableStateFlow?>(null)
+ val artistDescendentAlbums = _artistDescendentAlbums.asStateFlow()
+
//************************************************
// Functions
@@ -68,11 +74,12 @@ class AlbumsScreenVM(application: Application) : AndroidViewModel(application) {
fun loadScreen(mainVM: MainVM) {
- fun load(){
+ fun load() {
val sortType = preferences.getString(Settings.ALBUMS_SORT, Settings.Values.Sort.RECENT)
val albums = mainVM.songsData.value?.albums
+ val artists = mainVM.songsData.value?.artists
- if (albums != null) {
+ if (albums != null && artists != null) {
_recentAlbums.update { albums }
@@ -82,12 +89,19 @@ class AlbumsScreenVM(application: Application) : AndroidViewModel(application) {
_descendentAlbums.update { albums.sortedByDescending { it.title } }
+ _artistAscendentAlbums.update { albums.sortedBy { it.artistID } }
+
+ _artistDescendentAlbums.update { albums.sortedByDescending { it.artistID } }
+
+
_currentAlbums.update {
when (sortType) {
Settings.Values.Sort.RECENT -> recentAlbums.value
Settings.Values.Sort.OLDEST -> oldestAlbums.value
Settings.Values.Sort.ASCENDENT -> ascendentAlbums.value
- else -> descendentAlbums.value
+ Settings.Values.Sort.DESCENDENT -> descendentAlbums.value
+ Settings.Values.Sort.ARTIST_ASCENDENT -> artistAscendentAlbums.value
+ else -> artistDescendentAlbums.value
}
}
@@ -97,9 +111,9 @@ class AlbumsScreenVM(application: Application) : AndroidViewModel(application) {
load()
- viewModelScope.launch{
- withContext(Dispatchers.IO){
- mainVM.songsData.collect{
+ viewModelScope.launch {
+ withContext(Dispatchers.IO) {
+ mainVM.songsData.collect {
load()
filterAlbums()
}
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/artist/ArtistScreenVM.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/artist/ArtistScreenVM.kt
index 95a4128..1a615ff 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/artist/ArtistScreenVM.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/artist/ArtistScreenVM.kt
@@ -91,12 +91,17 @@ class ArtistScreenVM(application: Application) : AndroidViewModel(application) {
fun loadScreen(artistID: Long, mainVM: MainVM, settingsVM: SettingsVM) {
- fun load(){
+ fun load() {
val songsData = mainVM.songsData.value
val songs = songsData?.songs
val artists = songsData?.artists
val albums = songsData?.albums
+ val canDownloadArtistCover = settingsVM.downloadArtistCoverSetting.value
+ val isInternetAvailable = isNetworkAvailable(context)
+ val canDownloadOverData = settingsVM.downloadOverDataSetting.value
+ val isMobileDataEnabled = isOnMobileData(context)
+
if (songs != null && artists != null && albums != null) {
val artist = artists.first { it.id == artistID }
@@ -117,7 +122,7 @@ class ArtistScreenVM(application: Application) : AndroidViewModel(application) {
val newAlbums = ArrayList()
artistSongs.value?.forEach { song ->
- if( !newAlbums.any { it.id == song.albumID}){
+ if (!newAlbums.any { it.id == song.albumID }) {
newAlbums.add(albums.first { it.id == song.albumID })
}
}
@@ -127,22 +132,18 @@ class ArtistScreenVM(application: Application) : AndroidViewModel(application) {
_screenLoaded.update { true }
//Loads Artist Image
+
if (artistQuery!!.alreadyRequested) {
if (artistQuery.image != null) {
-
val imageBytes = Base64.decode(artistQuery.image, Base64.DEFAULT)
_tintCover.update { false }
_artistCover.update { BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size) }
}
- } else {
- val canDownloadArtistCover = settingsVM.downloadArtistCoverSetting.value
- val isInternetAvailable = isNetworkAvailable(context)
- val canDownloadOverData = settingsVM.downloadOverDataSetting.value
- val isMobileDataEnabled = isOnMobileData(context)
+ } else {
if (isInternetAvailable && canDownloadArtistCover) {
if ((canDownloadOverData && isMobileDataEnabled) || (!canDownloadOverData && !isMobileDataEnabled)) {
@@ -223,8 +224,8 @@ class ArtistScreenVM(application: Application) : AndroidViewModel(application) {
load()
viewModelScope.launch {
- withContext(Dispatchers.IO){
- mainVM.songsData.collect{
+ withContext(Dispatchers.IO) {
+ mainVM.songsData.collect {
load()
}
}
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/artist/artist_select_cover/SelectArtistCoverScreenVM.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/artist/artist_select_cover/SelectArtistCoverScreenVM.kt
index 5d3cb00..844c7ef 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/artist/artist_select_cover/SelectArtistCoverScreenVM.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/artist/artist_select_cover/SelectArtistCoverScreenVM.kt
@@ -107,6 +107,8 @@ class SelectArtistCoverScreenVM(application: Application) : AndroidViewModel(app
artistsCoversQueries.updateArtistCover(artistID, bitmapString)
+ artistsCoversQueries.updateArtistAlreadyRequested(artistID)
+
val artist = artistsCoversQueries.getArtist(artistID)
val imageBytes = Base64.decode(artist!!.image, Base64.DEFAULT)
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/home/HomeScreen.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/home/HomeScreen.kt
index 74bbe95..24f4044 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/home/HomeScreen.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/home/HomeScreen.kt
@@ -54,7 +54,7 @@ fun HomeScreen(
val descendentSongs = vm.descendentSongs.collectAsState().value
val syncingSongs = vm.syncingSongs.collectAsState().value
- val pullRefresState = rememberPullRefreshState(
+ val pullRefreshState = rememberPullRefreshState(
refreshing = syncingSongs,
onRefresh = {
vm.reloadSongs(mainVM)
@@ -119,7 +119,7 @@ fun HomeScreen(
.fillMaxSize()
.background(mainVM.surfaceColor.collectAsState().value)
.padding(scaffoldPadding)
- .pullRefresh(pullRefresState)
+ .pullRefresh(pullRefreshState)
) {
Column {
@@ -317,7 +317,7 @@ fun HomeScreen(
PullRefreshIndicator(
refreshing = syncingSongs,
- state = pullRefresState,
+ state = pullRefreshState,
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
contentColor = MaterialTheme.colorScheme.primary,
modifier = Modifier.align(Alignment.TopCenter)
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/home/HomeScreenVM.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/home/HomeScreenVM.kt
index 5aefc64..a6d70a5 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/home/HomeScreenVM.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/home/HomeScreenVM.kt
@@ -133,17 +133,22 @@ class HomeScreenVM(application: Application) : AndroidViewModel(application) {
fun reloadSongs(mainVM: MainVM){
+ _menuExpanded.update { false }
+
+ if(preferences.getBoolean("indexingSongs", false)) return
+
Toast.makeText(context, getAppString(context, R.string.IndexingSongs), Toast.LENGTH_LONG).show()
viewModelScope.launch {
withContext(Dispatchers.IO){
- _menuExpanded.update { false }
-
_syncingSongs.update { true }
+ preferences.edit().putBoolean("indexingSongs", true).apply()
+
mainVM.indexSongs(showNotification = true, onFinish = {
_syncingSongs.update { false }
+ preferences.edit().putBoolean("indexingSongs", false).apply()
})
}
}
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/main/MainScreen.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/main/MainScreen.kt
index 9fc645f..50e1f02 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/main/MainScreen.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/main/MainScreen.kt
@@ -67,6 +67,7 @@ import java.net.URLDecoder
@Composable
fun MainScreen(
mainVM: MainVM,
+ settingsVM: SettingsVM,
activityContext: ViewModelStoreOwner,
rootNavController: NavHostController,
onGetPlaylistImage: () -> Unit,
@@ -109,6 +110,7 @@ fun MainScreen(
context,
activityContext,
mainVM,
+ settingsVM,
hideNavbarMotionProgress,
playerSheetScaffoldState,
miniPlayerHeight,
@@ -126,6 +128,7 @@ fun MainScreen(
context,
activityContext,
mainVM,
+ settingsVM,
playerSheetScaffoldState,
miniPlayerHeight,
currentSong,
@@ -146,6 +149,7 @@ fun PortraitScreen(
context: Context,
activityContext: ViewModelStoreOwner,
mainVM: MainVM,
+ settingsVM: SettingsVM,
hideNavbarMotionProgress: Float,
playerSheetScaffoldState: BottomSheetScaffoldState,
miniPlayerHeight: Dp,
@@ -176,6 +180,7 @@ fun PortraitScreen(
MainScaffold(
activityContext,
mainVM,
+ settingsVM,
playerSheetScaffoldState,
miniPlayerHeight,
currentSong,
@@ -206,6 +211,7 @@ fun LandscapeScreen(
context: Context,
activityContext: ViewModelStoreOwner,
mainVM: MainVM,
+ settingsVM: SettingsVM,
playerSheetScaffoldState: BottomSheetScaffoldState,
miniPlayerHeight: Dp,
currentSong: Song?,
@@ -241,6 +247,7 @@ fun LandscapeScreen(
MainScaffold(
activityContext,
mainVM,
+ settingsVM,
playerSheetScaffoldState,
miniPlayerHeight,
currentSong,
@@ -260,6 +267,7 @@ fun LandscapeScreen(
fun MainScaffold(
activityContext: ViewModelStoreOwner,
mainVM: MainVM,
+ settingsVM: SettingsVM,
playerSheetState: BottomSheetScaffoldState,
miniPlayerHeight: Dp,
currentSong: Song?,
@@ -308,6 +316,7 @@ fun MainScaffold(
Player(
mainVM = mainVM,
+ settingsVM = settingsVM,
vm = ViewModelProvider(activityContext)[PlayerScreenVM::class.java],
selectedSong = currentSong,
queue = queue,
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/player/PlayerScreen.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/player/PlayerScreen.kt
index 87d4af7..9615b10 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/player/PlayerScreen.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/player/PlayerScreen.kt
@@ -2,11 +2,13 @@ package com.lighttigerxiv.simple.mp.compose.screens.main.player
import android.content.Context
import android.content.res.Configuration
+import android.view.View
import androidx.compose.foundation.*
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsDraggedAsState
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
@@ -20,11 +22,13 @@ import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.compose.ui.viewinterop.AndroidView
import androidx.constraintlayout.compose.ExperimentalMotionApi
import androidx.constraintlayout.compose.MotionLayout
import androidx.constraintlayout.compose.MotionScene
@@ -42,14 +46,18 @@ import com.lighttigerxiv.simple.mp.compose.data.variables.SMALL_SPACING
import com.lighttigerxiv.simple.mp.compose.data.variables.XSMALL_SPACING
import com.lighttigerxiv.simple.mp.compose.functions.getImage
import com.lighttigerxiv.simple.mp.compose.screens.main.playlists.playlist.modifyIf
+import com.lighttigerxiv.simple.mp.compose.settings.SettingsVM
import com.lighttigerxiv.simple.mp.compose.ui.composables.ClickableMediumIcon
import com.lighttigerxiv.simple.mp.compose.ui.composables.CustomText
import com.lighttigerxiv.simple.mp.compose.ui.composables.ReorderableSongItem
+import com.lighttigerxiv.simple.mp.compose.ui.composables.spacers.MediumHorizontalSpacer
+import com.lighttigerxiv.simple.mp.compose.ui.composables.spacers.MediumVerticalSpacer
import com.lighttigerxiv.simple.mp.compose.ui.composables.spacers.SmallVerticalSpacer
import com.lighttigerxiv.simple.mp.compose.ui.composables.spacers.SmallHorizontalSpacer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
+import org.burnoutcrew.reorderable.ItemPosition
import org.burnoutcrew.reorderable.ReorderableItem
import org.burnoutcrew.reorderable.rememberReorderableLazyListState
import org.burnoutcrew.reorderable.reorderable
@@ -58,6 +66,7 @@ import org.burnoutcrew.reorderable.reorderable
@Composable
fun Player(
mainVM: MainVM,
+ settingsVM: SettingsVM,
vm: PlayerScreenVM,
selectedSong: Song,
queue: List?,
@@ -81,6 +90,8 @@ fun Player(
val inPortrait = localConfiguration.orientation == Configuration.ORIENTATION_PORTRAIT
val showMenu = vm.showMenu.collectAsState().value
val hideNavMotionProgress = mainVM.hideNavProgress.collectAsState().value
+ val showCarPlayer = mainVM.showCarPlayer.collectAsState().value
+ val keepScreenOnInCarMode = settingsVM.keepScreenOnInCarModeSetting.collectAsState().value
LaunchedEffect(queue) {
songsCoversPager.scrollToPage(mainVM.songPosition.value)
@@ -125,10 +136,14 @@ fun Player(
if (currentTab == 1) {
vm.highlightTab("queue")
+
}
}
}
+
+
+
MotionLayout(
modifier = Modifier
.fillMaxSize(),
@@ -158,49 +173,105 @@ fun Player(
if (inPortrait) {
- PortraitPlayer(
- context,
- scope,
- vm,
- mainVM,
- surfaceColor,
- selectedSong,
- queue,
- upNextQueue,
- musicPlaying,
- songSeconds,
- songAndQueuePager,
- songsCoversPager,
- showMenu,
- onOpenPage = { onOpenPage(it) },
- currentSongMinutesAndSecondsText,
- songMinutesAndSecondsText,
- queueShuffled,
- songOnRepeat,
- onClosePLayer = { onClosePLayer() }
- )
+
+ if (showCarPlayer) {
+ PortraitCarPlayer(
+ context,
+ scope,
+ vm,
+ mainVM,
+ selectedSong,
+ queue,
+ upNextQueue,
+ musicPlaying,
+ songSeconds,
+ songAndQueuePager,
+ songsCoversPager,
+ currentSongMinutesAndSecondsText,
+ songMinutesAndSecondsText,
+ queueShuffled,
+ songOnRepeat,
+ onClosePLayer = { onClosePLayer() }
+ )
+
+ if(keepScreenOnInCarMode){
+ KeepScreenOn()
+ }
+ } else {
+ PortraitPlayer(
+ context,
+ scope,
+ vm,
+ mainVM,
+ settingsVM,
+ surfaceColor,
+ selectedSong,
+ queue,
+ upNextQueue,
+ musicPlaying,
+ songSeconds,
+ songAndQueuePager,
+ songsCoversPager,
+ showMenu,
+ onOpenPage = { onOpenPage(it) },
+ currentSongMinutesAndSecondsText,
+ songMinutesAndSecondsText,
+ queueShuffled,
+ songOnRepeat,
+ onClosePLayer = { onClosePLayer() },
+ showCarPlayer
+ )
+ }
+
} else {
- LandscapePlayer(
- context,
- scope,
- vm,
- mainVM,
- surfaceColor,
- selectedSong,
- queue,
- upNextQueue,
- musicPlaying,
- songSeconds,
- songAndQueuePager,
- songsCoversPager,
- showMenu,
- onOpenPage = { onOpenPage(it) },
- currentSongMinutesAndSecondsText,
- songMinutesAndSecondsText,
- queueShuffled,
- songOnRepeat,
- onClosePLayer = { onClosePLayer() }
- )
+
+ if(showCarPlayer){
+ LandscapeCarPlayer(
+ context,
+ scope,
+ vm,
+ mainVM,
+ selectedSong,
+ queue,
+ upNextQueue,
+ musicPlaying,
+ songSeconds,
+ songAndQueuePager,
+ songsCoversPager,
+ currentSongMinutesAndSecondsText,
+ songMinutesAndSecondsText,
+ queueShuffled,
+ songOnRepeat,
+ onClosePLayer = { onClosePLayer() }
+ )
+
+ if(keepScreenOnInCarMode){
+ KeepScreenOn()
+ }
+ }else{
+ LandscapePlayer(
+ context,
+ scope,
+ vm,
+ mainVM,
+ settingsVM,
+ surfaceColor,
+ selectedSong,
+ queue,
+ upNextQueue,
+ musicPlaying,
+ songSeconds,
+ songAndQueuePager,
+ songsCoversPager,
+ showMenu,
+ onOpenPage = { onOpenPage(it) },
+ currentSongMinutesAndSecondsText,
+ songMinutesAndSecondsText,
+ queueShuffled,
+ songOnRepeat,
+ onClosePLayer = { onClosePLayer() }
+ )
+ }
}
}
}
@@ -215,6 +286,7 @@ fun PortraitPlayer(
scope: CoroutineScope,
vm: PlayerScreenVM,
mainVM: MainVM,
+ settingsVM: SettingsVM,
surfaceColor: Color,
currentSong: Song,
queue: List?,
@@ -229,7 +301,8 @@ fun PortraitPlayer(
songMinutesAndSecondsText: String,
queueShuffled: Boolean,
songOnRepeat: Boolean,
- onClosePLayer: () -> Unit
+ onClosePLayer: () -> Unit,
+ showCarPlayer: Boolean
) {
val defaultAlbumArt = remember { getImage(context, R.drawable.cd, ImageSizes.LARGE).asImageBitmap() }
@@ -275,7 +348,6 @@ fun PortraitPlayer(
.background(MaterialTheme.colorScheme.surface)
) {
-
Row(
modifier = Modifier
.clip(CircleShape)
@@ -372,7 +444,9 @@ fun PortraitPlayer(
.background(mainVM.surfaceColor.collectAsState().value),
expanded = showMenu,
onDismissRequest = {
- vm.updateShowMenu(false)
+ if (!showCarPlayer) {
+ vm.updateShowMenu(false)
+ }
},
) {
@@ -409,6 +483,18 @@ fun PortraitPlayer(
vm.updateShowMenu(false)
}
)
+
+ if (settingsVM.carPlayerSetting.collectAsState().value) {
+ DropdownMenuItem(
+ text = {
+ CustomText(text = stringResource(id = R.string.ShowCarPlayer))
+ },
+ onClick = {
+
+ mainVM.updateShowCarPlayer(true)
+ }
+ )
+ }
}
}
}
@@ -675,14 +761,17 @@ fun PortraitPlayer(
.fillMaxSize()
.then(Modifier.reorderable(state))
) {
- items(items = upNextQueue, key = { it.id }) { song ->
+ itemsIndexed(items = upNextQueue, key = { _, song -> song.id }) { index, song ->
ReorderableItem(state, key = song.id) { isDragging ->
ReorderableSongItem(
mainVM = mainVM,
song = song,
state = state,
- isDragging = isDragging
+ isDragging = isDragging,
+ onClick = {
+ mainVM.onUpNextQueueMove(ItemPosition(index, song.id), ItemPosition(0, upNextQueue[0].id))
+ }
)
}
}
@@ -696,12 +785,11 @@ fun PortraitPlayer(
@OptIn(ExperimentalPagerApi::class)
@Composable
-fun LandscapePlayer(
+fun PortraitCarPlayer(
context: Context,
scope: CoroutineScope,
vm: PlayerScreenVM,
mainVM: MainVM,
- surfaceColor: Color,
currentSong: Song,
queue: List?,
upNextQueue: List?,
@@ -709,8 +797,6 @@ fun LandscapePlayer(
songSeconds: Float,
songAndQueuePager: PagerState,
songsCoversPager: PagerState,
- showMenu: Boolean,
- onOpenPage: (page: String) -> Unit,
currentSongMinutesAndSecondsText: String,
songMinutesAndSecondsText: String,
queueShuffled: Boolean,
@@ -718,7 +804,6 @@ fun LandscapePlayer(
onClosePLayer: () -> Unit
) {
- val defaultAlbumArt = remember { getImage(context, R.drawable.cd, ImageSizes.LARGE).asImageBitmap() }
var secondsSliderValue by remember { mutableStateOf(songSeconds) }
val secondsSliderInteractionSource = remember { MutableInteractionSource() }
val draggingSecondsSlider = secondsSliderInteractionSource.collectIsDraggedAsState().value
@@ -732,14 +817,13 @@ fun LandscapePlayer(
}
Column(
- modifier = Modifier
- .fillMaxSize()
+ modifier = Modifier.fillMaxSize()
) {
Row(
modifier = Modifier
- .fillMaxWidth()
-
+ .fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically
) {
ClickableMediumIcon(
@@ -748,6 +832,8 @@ fun LandscapePlayer(
color = MaterialTheme.colorScheme.onSurface
)
+ MediumHorizontalSpacer()
+
Row(
modifier = Modifier
.fillMaxWidth()
@@ -772,12 +858,13 @@ fun LandscapePlayer(
}
}
.padding(
- top = 12.dp,
- bottom = 12.dp,
+ top = 10.dp,
+ bottom = 10.dp,
start = MEDIUM_SPACING,
end = MEDIUM_SPACING
),
- verticalAlignment = Alignment.CenterVertically
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
) {
Icon(
@@ -809,12 +896,13 @@ fun LandscapePlayer(
}
}
.padding(
- top = 12.dp,
- bottom = 12.dp,
+ top = 10.dp,
+ bottom = 10.dp,
start = MEDIUM_SPACING,
end = MEDIUM_SPACING
),
- verticalAlignment = Alignment.CenterVertically
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
) {
Icon(
@@ -837,6 +925,8 @@ fun LandscapePlayer(
}
}
+ MediumHorizontalSpacer()
+
Box(
modifier = Modifier
.height(35.dp)
@@ -845,147 +935,1058 @@ fun LandscapePlayer(
if (songAndQueuePager.currentPage == 0) {
ClickableMediumIcon(
- id = R.drawable.vertical_three_dots,
+ id = R.drawable.smartphone,
onClick = {
- vm.updateShowMenu(true)
+
+ mainVM.updateShowCarPlayer(false)
+ vm.updateShowMenu(false)
},
color = MaterialTheme.colorScheme.onSurface
)
+ }
+ }
+ }
+ HorizontalPager(
+ modifier = Modifier.fillMaxSize(),
+ state = songAndQueuePager
+ ) { selectedPage ->
- DropdownMenu(
+ if (selectedPage == 0) {
+ Column {
+
+ Column(
modifier = Modifier
- .background(mainVM.surfaceColor.collectAsState().value),
- expanded = showMenu,
- onDismissRequest = {
- vm.updateShowMenu(false)
- },
+ .fillMaxWidth()
+ .fillMaxHeight()
+ .weight(1f)
) {
+ HorizontalPager(
+ state = songsCoversPager,
+ itemSpacing = SMALL_SPACING,
+ ) { currentCoverIndex ->
- DropdownMenuItem(
- text = {
- CustomText(text = stringResource(id = R.string.GoToArtist))
- },
- onClick = {
-
- onOpenPage("${Routes.Root.FLOATING_ARTIST}${currentSong.artistID}")
- vm.updateShowMenu(false)
- }
- )
-
- DropdownMenuItem(
- text = {
- CustomText(text = stringResource(id = R.string.GoToAlbum))
- },
- onClick = {
-
- onOpenPage("${Routes.Root.FLOATING_ALBUM}${currentSong.albumID}")
- vm.updateShowMenu(false)
- }
- )
-
- DropdownMenuItem(
- text = {
- CustomText(text = stringResource(id = R.string.AddToPlaylist))
- },
- onClick = {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ queue?.get(currentCoverIndex)?.let {
+ CustomText(
+ modifier = Modifier,
+ text = it.title,
+ size = 40.sp,
+ color = MaterialTheme.colorScheme.primary
+ )
+ }
- onOpenPage("${Routes.Root.ADD_SONG_TO_PLAYLIST}${currentSong.id}")
+ MediumVerticalSpacer()
- vm.updateShowMenu(false)
+ queue?.get(currentCoverIndex)?.let {
+ CustomText(
+ text = mainVM.getSongArtist(it).name,
+ size = 30.sp
+ )
+ }
}
- )
+ }
}
- }
- }
- }
- Spacer(modifier = Modifier.height(32.dp))
+ Slider(
+ value = secondsSliderValue,
+ interactionSource = secondsSliderInteractionSource,
+ onValueChange = {
- HorizontalPager(
- modifier = Modifier.fillMaxSize(),
- state = songAndQueuePager
- ) { selectedPage ->
+ secondsSliderValue = it
+ mainVM.updateCurrentSongMinutesAndSecondsText(mainVM.getMinutesAndSeconds(it.toInt()))
+ },
+ onValueChangeFinished = {
- Row(
- modifier = Modifier.fillMaxSize()
- ) {
+ if (!musicPlaying)
+ mainVM.pauseResumeMusic()
- //Song Tab
- if (selectedPage == 0) {
+ mainVM.seekSongSeconds(secondsSliderValue.toInt())
+ },
+ valueRange = 1f..(currentSong.duration / 1000).toFloat(),
+ colors = SliderDefaults.colors(
+ thumbColor = MaterialTheme.colorScheme.primary,
+ activeTrackColor = MaterialTheme.colorScheme.primary,
+ inactiveTrackColor = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ )
Row(
modifier = Modifier
- .weight(0.4f)
- .fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.Center
+ .fillMaxWidth()
+ .wrapContentHeight()
) {
- HorizontalPager(
- state = songsCoversPager,
- itemSpacing = SMALL_SPACING
- ) { currentCoverIndex ->
+ Text(
+ modifier = Modifier.offset(x = 8.dp),
+ text = currentSongMinutesAndSecondsText,
+ color = MaterialTheme.colorScheme.onSurface
+ )
- val songAlbumArt = mainVM.getSongArt(queue!![currentCoverIndex])
+ Spacer(modifier = Modifier.weight(1f, fill = true))
- Image(
- modifier = Modifier
- .fillMaxHeight()
- .aspectRatio(1f)
- .clip(RoundedCornerShape(14.dp))
- .modifyIf(songAlbumArt == null) {
- background(surfaceColor)
- }
- .modifyIf(songAlbumArt == null) {
- padding(5.dp)
- },
- bitmap = songAlbumArt?.asImageBitmap() ?: defaultAlbumArt,
- colorFilter = if (songAlbumArt == null) ColorFilter.tint(MaterialTheme.colorScheme.primary) else null,
- contentDescription = "Album Art"
- )
- }
+ Text(
+ modifier = Modifier.offset(x = (-8).dp),
+ text = songMinutesAndSecondsText,
+ color = MaterialTheme.colorScheme.onSurface
+ )
}
+ MediumVerticalSpacer()
+
Column(
modifier = Modifier
- .weight(0.6f)
- .fillMaxWidth()
.fillMaxHeight()
+ .weight(1f)
) {
- Column(
+ Row(
modifier = Modifier
- .fillMaxWidth()
- ) {
-
- CustomText(
- text = currentSong.title,
- size = 24.sp,
- weight = FontWeight.Bold,
- color = MaterialTheme.colorScheme.primary
- )
+ .fillMaxHeight()
+ .weight(1f, fill = true)
+ ){
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f, fill = true),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ){
+ Icon(
+ painter = painterResource(id = R.drawable.previous),
+ contentDescription = "Select Previous Song",
+ tint = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(20.dp)
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+ mainVM.selectPreviousSong()
+ }
+ )
+ }
- CustomText(
+ Row(
modifier = Modifier
- .clickable { onOpenPage("${Routes.Root.FLOATING_ARTIST}${currentSong.artistID}") },
- text = mainVM.getSongArtist(currentSong).name,
- size = 16.sp
- )
- }
+ .fillMaxWidth()
+ .weight(1f, fill = true),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ){
+ Icon(
+ bitmap = if (musicPlaying) {
+ remember { getImage(context, R.drawable.icon_pause_round_solid, ImageSizes.MEDIUM) }
+ } else {
+ remember { getImage(context, R.drawable.icon_play_round_solid, ImageSizes.MEDIUM) }
+ }.asImageBitmap(),
+ contentDescription = "Play/Pause Button",
+ tint = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier
+ .fillMaxSize()
+ .aspectRatio(1f)
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+ mainVM.pauseResumeMusic()
+ }
+ )
+ }
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f, fill = true),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ){
+ Icon(
+ painter = painterResource(id = R.drawable.next),
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier
+ .padding(20.dp)
+ .fillMaxSize()
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
- Slider(
- value = secondsSliderValue,
- interactionSource = secondsSliderInteractionSource,
- onValueChange = {
+ mainVM.selectNextSong()
+ }
+ )
+ }
+ }
- secondsSliderValue = it
- mainVM.updateCurrentSongMinutesAndSecondsText(mainVM.getMinutesAndSeconds(it.toInt()))
- },
- onValueChangeFinished = {
+ Row(
+ modifier = Modifier
+ .fillMaxHeight()
+ .weight(1f, fill = true)
+ .padding(40.dp),
+ ){
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f, fill = true),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ){
- if (!musicPlaying)
+ Icon(
+ painter = painterResource(id = R.drawable.shuffle),
+ contentDescription = "",
+ tint = if (queueShuffled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier
+ .fillMaxHeight()
+ .weight(1f, fill = true)
+ .aspectRatio(1f)
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+
+ mainVM.toggleShuffle()
+ }
+ )
+
+ if (queueShuffled) {
+ Dot(carMode = true)
+ }else{
+ Spacer(modifier = Modifier.height(8.dp))
+ }
+ }
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f, fill = true),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ){
+
+ Icon(
+ painter = painterResource(id = R.drawable.repeat),
+ contentDescription = "Repeat Song",
+ tint = if (songOnRepeat) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier
+ .fillMaxHeight()
+ .weight(1f, fill = true)
+ .aspectRatio(1f)
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+ mainVM.toggleRepeat()
+ }
+ )
+
+ if (songOnRepeat) {
+ Dot(carMode = true)
+ }else{
+ Spacer(modifier = Modifier.height(8.dp))
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (selectedPage == 1) {
+
+ Column {
+
+ MediumVerticalSpacer()
+
+ if (upNextQueue!!.isEmpty()) {
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize(),
+ verticalArrangement = Arrangement.Top,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+
+ SmallVerticalSpacer()
+
+ CustomText(text = stringResource(id = R.string.Shrug))
+ }
+ } else {
+
+ val state = rememberReorderableLazyListState(onMove = mainVM::onUpNextQueueMove)
+
+ androidx.compose.foundation.lazy.LazyColumn(
+ state = state.listState,
+ modifier = Modifier
+ .fillMaxSize()
+ .then(Modifier.reorderable(state))
+ ) {
+ itemsIndexed(items = upNextQueue, key = {_, song -> song.id }) { index, song ->
+ ReorderableItem(state, key = song.id) { isDragging ->
+
+ ReorderableSongItem(
+ mainVM = mainVM,
+ song = song,
+ state = state,
+ isDragging = isDragging,
+ carMode = true,
+ onClick = {
+ mainVM.onUpNextQueueMove(ItemPosition(index, song.id), ItemPosition(0, upNextQueue[0].id))
+ }
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+@OptIn(ExperimentalPagerApi::class)
+@Composable
+fun LandscapePlayer(
+ context: Context,
+ scope: CoroutineScope,
+ vm: PlayerScreenVM,
+ mainVM: MainVM,
+ settingsVM: SettingsVM,
+ surfaceColor: Color,
+ currentSong: Song,
+ queue: List?,
+ upNextQueue: List?,
+ musicPlaying: Boolean,
+ songSeconds: Float,
+ songAndQueuePager: PagerState,
+ songsCoversPager: PagerState,
+ showMenu: Boolean,
+ onOpenPage: (page: String) -> Unit,
+ currentSongMinutesAndSecondsText: String,
+ songMinutesAndSecondsText: String,
+ queueShuffled: Boolean,
+ songOnRepeat: Boolean,
+ onClosePLayer: () -> Unit
+) {
+
+ val defaultAlbumArt = remember { getImage(context, R.drawable.cd, ImageSizes.LARGE).asImageBitmap() }
+ var secondsSliderValue by remember { mutableStateOf(songSeconds) }
+ val secondsSliderInteractionSource = remember { MutableInteractionSource() }
+ val draggingSecondsSlider = secondsSliderInteractionSource.collectIsDraggedAsState().value
+
+ LaunchedEffect(songSeconds) {
+ if (!draggingSecondsSlider) {
+ secondsSliderValue = songSeconds
+ }
+
+ mainVM.updateCurrentSongMinutesAndSecondsText(mainVM.getMinutesAndSeconds((secondsSliderValue).toInt()))
+ }
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ ) {
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+
+ ) {
+
+ ClickableMediumIcon(
+ id = R.drawable.icon_arrow_down_solid,
+ onClick = { onClosePLayer() },
+ color = MaterialTheme.colorScheme.onSurface
+ )
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f, fill = true),
+ horizontalArrangement = Arrangement.Center
+ ) {
+
+ Row(
+ modifier = Modifier
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.surface)
+ ) {
+
+
+ Row(
+ modifier = Modifier
+ .clip(CircleShape)
+ .background(if (songAndQueuePager.currentPage == 0) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface)
+ .clickable {
+ scope.launch {
+ songAndQueuePager.scrollToPage(0)
+ }
+ }
+ .padding(
+ top = 12.dp,
+ bottom = 12.dp,
+ start = MEDIUM_SPACING,
+ end = MEDIUM_SPACING
+ ),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+
+ Icon(
+ modifier = Modifier
+ .height(18.dp)
+ .width(18.dp),
+ painter = painterResource(id = R.drawable.music_note),
+ contentDescription = null,
+ tint = if (songAndQueuePager.currentPage == 0) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.onSurface
+ )
+
+ SmallHorizontalSpacer()
+
+ CustomText(
+
+ text = "Song",
+ color = if (songAndQueuePager.currentPage == 0) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.onSurface
+ )
+ }
+
+
+ Row(
+ modifier = Modifier
+ .clip(CircleShape)
+ .background(if (songAndQueuePager.currentPage == 1) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface)
+ .clickable {
+ scope.launch {
+ songAndQueuePager.scrollToPage(1)
+ }
+ }
+ .padding(
+ top = 12.dp,
+ bottom = 12.dp,
+ start = MEDIUM_SPACING,
+ end = MEDIUM_SPACING
+ ),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+
+ Icon(
+ modifier = Modifier
+ .height(18.dp)
+ .width(18.dp),
+ painter = painterResource(id = R.drawable.queue_list),
+ contentDescription = null,
+ tint = if (songAndQueuePager.currentPage == 1) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.onSurface
+ )
+
+ SmallHorizontalSpacer()
+
+ CustomText(
+
+ text = "Up Next",
+ color = if (songAndQueuePager.currentPage == 1) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.onSurface
+ )
+ }
+ }
+ }
+
+ Box(
+ modifier = Modifier
+ .height(35.dp)
+ .width(35.dp)
+ ) {
+
+ if (songAndQueuePager.currentPage == 0) {
+ ClickableMediumIcon(
+ id = R.drawable.vertical_three_dots,
+ onClick = {
+ vm.updateShowMenu(true)
+ },
+ color = MaterialTheme.colorScheme.onSurface
+ )
+
+
+ DropdownMenu(
+ modifier = Modifier
+ .background(mainVM.surfaceColor.collectAsState().value),
+ expanded = showMenu,
+ onDismissRequest = {
+ vm.updateShowMenu(false)
+ },
+ ) {
+
+ DropdownMenuItem(
+ text = {
+ CustomText(text = stringResource(id = R.string.GoToArtist))
+ },
+ onClick = {
+
+ onOpenPage("${Routes.Root.FLOATING_ARTIST}${currentSong.artistID}")
+ vm.updateShowMenu(false)
+ }
+ )
+
+ DropdownMenuItem(
+ text = {
+ CustomText(text = stringResource(id = R.string.GoToAlbum))
+ },
+ onClick = {
+
+ onOpenPage("${Routes.Root.FLOATING_ALBUM}${currentSong.albumID}")
+ vm.updateShowMenu(false)
+ }
+ )
+
+ DropdownMenuItem(
+ text = {
+ CustomText(text = stringResource(id = R.string.AddToPlaylist))
+ },
+ onClick = {
+
+ onOpenPage("${Routes.Root.ADD_SONG_TO_PLAYLIST}${currentSong.id}")
+
+ vm.updateShowMenu(false)
+ }
+ )
+
+ if (settingsVM.carPlayerSetting.collectAsState().value) {
+ DropdownMenuItem(
+ text = {
+ CustomText(text = stringResource(id = R.string.ShowCarPlayer))
+ },
+ onClick = {
+
+ mainVM.updateShowCarPlayer(true)
+ }
+ )
+ }
+ }
+ }
+ }
+ }
+
+ Spacer(modifier = Modifier.height(32.dp))
+
+ HorizontalPager(
+ modifier = Modifier.fillMaxSize(),
+ state = songAndQueuePager
+ ) { selectedPage ->
+
+ Row(
+ modifier = Modifier.fillMaxSize()
+ ) {
+
+ //Song Tab
+ if (selectedPage == 0) {
+
+ Row(
+ modifier = Modifier
+ .weight(0.4f)
+ .fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ) {
+
+ HorizontalPager(
+ state = songsCoversPager,
+ itemSpacing = SMALL_SPACING
+ ) { currentCoverIndex ->
+
+ val songAlbumArt = mainVM.getSongArt(queue!![currentCoverIndex])
+
+ Image(
+ modifier = Modifier
+ .fillMaxHeight()
+ .aspectRatio(1f)
+ .clip(RoundedCornerShape(14.dp))
+ .modifyIf(songAlbumArt == null) {
+ background(surfaceColor)
+ }
+ .modifyIf(songAlbumArt == null) {
+ padding(5.dp)
+ },
+ bitmap = songAlbumArt?.asImageBitmap() ?: defaultAlbumArt,
+ colorFilter = if (songAlbumArt == null) ColorFilter.tint(MaterialTheme.colorScheme.primary) else null,
+ contentDescription = "Album Art"
+ )
+ }
+ }
+
+ Column(
+ modifier = Modifier
+ .weight(0.6f)
+ .fillMaxWidth()
+ .fillMaxHeight()
+ ) {
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ ) {
+
+ CustomText(
+ text = currentSong.title,
+ size = 24.sp,
+ weight = FontWeight.Bold,
+ color = MaterialTheme.colorScheme.primary
+ )
+
+ CustomText(
+ modifier = Modifier
+ .clickable { onOpenPage("${Routes.Root.FLOATING_ARTIST}${currentSong.artistID}") },
+ text = mainVM.getSongArtist(currentSong).name,
+ size = 16.sp
+ )
+ }
+
+
+ Slider(
+ value = secondsSliderValue,
+ interactionSource = secondsSliderInteractionSource,
+ onValueChange = {
+
+ secondsSliderValue = it
+ mainVM.updateCurrentSongMinutesAndSecondsText(mainVM.getMinutesAndSeconds(it.toInt()))
+ },
+ onValueChangeFinished = {
+
+ if (!musicPlaying)
+ mainVM.pauseResumeMusic()
+
+ mainVM.seekSongSeconds(secondsSliderValue.toInt())
+ },
+ valueRange = 1f..(currentSong.duration / 1000).toFloat(),
+ colors = SliderDefaults.colors(
+ thumbColor = MaterialTheme.colorScheme.primary,
+ activeTrackColor = MaterialTheme.colorScheme.primary,
+ inactiveTrackColor = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ )
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .wrapContentHeight()
+ ) {
+
+ Text(
+ modifier = Modifier.offset(x = 8.dp),
+ text = currentSongMinutesAndSecondsText,
+ color = MaterialTheme.colorScheme.onSurface
+ )
+
+ Spacer(modifier = Modifier.weight(1f, fill = true))
+
+ Text(
+ modifier = Modifier.offset(x = (-8).dp),
+ text = songMinutesAndSecondsText,
+ color = MaterialTheme.colorScheme.onSurface
+ )
+ }
+
+ Row(
+ modifier = Modifier
+ .wrapContentHeight()
+ .fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceEvenly,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier.wrapContentHeight()
+ ) {
+
+ if (queueShuffled) {
+ Spacer(modifier = Modifier.height(XSMALL_SPACING)) //Needed to keep shuffle button in place when shuffle is enabled
+ }
+
+ Icon(
+ painter = painterResource(id = R.drawable.shuffle),
+ contentDescription = "",
+ tint = if (queueShuffled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier
+ .size(35.dp)
+ .fillMaxSize()
+ .fillMaxSize()
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+
+ mainVM.toggleShuffle()
+ }
+ )
+
+ if (queueShuffled) {
+ Dot()
+ }
+ }
+
+ Icon(
+ painter = painterResource(id = R.drawable.previous),
+ contentDescription = "Select Previous Song",
+ tint = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier
+ .size(35.dp)
+ .fillMaxSize()
+ .fillMaxSize()
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+ mainVM.selectPreviousSong()
+ }
+ )
+
+ Icon(
+ painter = if (musicPlaying) {
+ painterResource(id = R.drawable.icon_pause_round_solid)
+ } else {
+ painterResource(id = R.drawable.icon_play_round_solid)
+ },
+ contentDescription = "Play/Pause Button",
+ tint = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier
+ .size(80.dp)
+ .fillMaxSize()
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+ mainVM.pauseResumeMusic()
+ }
+ )
+
+ Icon(
+ painter = painterResource(id = R.drawable.next),
+ contentDescription = "Next Song",
+ tint = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier
+ .size(35.dp)
+ .fillMaxSize()
+ .fillMaxSize()
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+
+ mainVM.selectNextSong()
+ }
+ )
+
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier.wrapContentHeight()
+ ) {
+
+ if (songOnRepeat) {
+
+ Spacer(modifier = Modifier.height(XSMALL_SPACING)) //Needed to keep repeat button in place when repeat is enabled
+ }
+
+ Icon(
+ painter = painterResource(id = R.drawable.repeat),
+ contentDescription = "Repeat Song",
+ tint = if (songOnRepeat) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier
+ .size(35.dp)
+ .fillMaxSize()
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+ mainVM.toggleRepeat()
+ }
+ )
+
+ if (songOnRepeat) {
+ Dot()
+ }
+ }
+ }
+
+ }
+
+ }
+
+ //Queue Tab
+ if (selectedPage == 1) {
+
+ if (upNextQueue!!.isEmpty()) {
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize(),
+ verticalArrangement = Arrangement.Top,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+
+ SmallVerticalSpacer()
+
+ CustomText(text = stringResource(id = R.string.Shrug))
+ }
+ } else {
+
+ val state = rememberReorderableLazyListState(onMove = mainVM::onUpNextQueueMove)
+
+ androidx.compose.foundation.lazy.LazyColumn(
+ state = state.listState,
+ modifier = Modifier
+ .fillMaxSize()
+ .then(Modifier.reorderable(state))
+ ) {
+ itemsIndexed(upNextQueue, { _, song -> song.id }) { index, song ->
+ ReorderableItem(state, key = song.id) { isDragging ->
+
+ ReorderableSongItem(
+ mainVM = mainVM,
+ song = song,
+ state = state,
+ isDragging = isDragging,
+ onClick = {
+ mainVM.onUpNextQueueMove(ItemPosition(index, song.id), ItemPosition(0, upNextQueue[0].id))
+ }
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+@OptIn(ExperimentalPagerApi::class)
+@Composable
+fun LandscapeCarPlayer(
+ context: Context,
+ scope: CoroutineScope,
+ vm: PlayerScreenVM,
+ mainVM: MainVM,
+ currentSong: Song,
+ queue: List?,
+ upNextQueue: List?,
+ musicPlaying: Boolean,
+ songSeconds: Float,
+ songAndQueuePager: PagerState,
+ songsCoversPager: PagerState,
+ currentSongMinutesAndSecondsText: String,
+ songMinutesAndSecondsText: String,
+ queueShuffled: Boolean,
+ songOnRepeat: Boolean,
+ onClosePLayer: () -> Unit
+) {
+
+ var secondsSliderValue by remember { mutableStateOf(songSeconds) }
+ val secondsSliderInteractionSource = remember { MutableInteractionSource() }
+ val draggingSecondsSlider = secondsSliderInteractionSource.collectIsDraggedAsState().value
+
+ LaunchedEffect(songSeconds) {
+ if (!draggingSecondsSlider) {
+ secondsSliderValue = songSeconds
+ }
+
+ mainVM.updateCurrentSongMinutesAndSecondsText(mainVM.getMinutesAndSeconds((secondsSliderValue).toInt()))
+ }
+
+ Column(
+ modifier = Modifier.fillMaxSize()
+ ) {
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+
+ ClickableMediumIcon(
+ id = R.drawable.icon_arrow_down_solid,
+ onClick = { onClosePLayer() },
+ color = MaterialTheme.colorScheme.onSurface
+ )
+
+ MediumHorizontalSpacer()
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f, fill = true),
+ horizontalArrangement = Arrangement.Center
+ ) {
+
+ Row(
+ modifier = Modifier
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.surface)
+ ) {
+
+
+ Row(
+ modifier = Modifier
+ .clip(CircleShape)
+ .background(if (songAndQueuePager.currentPage == 0) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface)
+ .clickable {
+ scope.launch {
+ songAndQueuePager.scrollToPage(0)
+ }
+ }
+ .padding(
+ top = 10.dp,
+ bottom = 10.dp,
+ start = MEDIUM_SPACING,
+ end = MEDIUM_SPACING
+ ),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ) {
+
+ Icon(
+ modifier = Modifier
+ .height(18.dp)
+ .width(18.dp),
+ painter = painterResource(id = R.drawable.music_note),
+ contentDescription = null,
+ tint = if (songAndQueuePager.currentPage == 0) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.onSurface
+ )
+
+ SmallHorizontalSpacer()
+
+ CustomText(
+
+ text = "Song",
+ color = if (songAndQueuePager.currentPage == 0) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.onSurface
+ )
+ }
+
+
+ Row(
+ modifier = Modifier
+ .clip(CircleShape)
+ .background(if (songAndQueuePager.currentPage == 1) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface)
+ .clickable {
+ scope.launch {
+ songAndQueuePager.scrollToPage(1)
+ }
+ }
+ .padding(
+ top = 10.dp,
+ bottom = 10.dp,
+ start = MEDIUM_SPACING,
+ end = MEDIUM_SPACING
+ ),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ) {
+
+ Icon(
+ modifier = Modifier
+ .height(18.dp)
+ .width(18.dp),
+ painter = painterResource(id = R.drawable.queue_list),
+ contentDescription = null,
+ tint = if (songAndQueuePager.currentPage == 1) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.onSurface
+ )
+
+ SmallHorizontalSpacer()
+
+ CustomText(
+
+ text = "Up Next",
+ color = if (songAndQueuePager.currentPage == 1) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.onSurface
+ )
+ }
+ }
+ }
+
+ MediumHorizontalSpacer()
+
+ Box(
+ modifier = Modifier
+ .height(35.dp)
+ .width(35.dp)
+ ) {
+
+ if (songAndQueuePager.currentPage == 0) {
+ ClickableMediumIcon(
+ id = R.drawable.smartphone,
+ onClick = {
+ mainVM.updateShowCarPlayer(false)
+ vm.updateShowMenu(false)
+ },
+ color = MaterialTheme.colorScheme.onSurface
+ )
+ }
+ }
+ }
+
+ HorizontalPager(
+ modifier = Modifier.fillMaxSize(),
+ state = songAndQueuePager
+ ) { selectedPage ->
+
+ if (selectedPage == 0) {
+ Row {
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .fillMaxHeight()
+ .weight(1f)
+ ) {
+ HorizontalPager(
+ state = songsCoversPager,
+ itemSpacing = SMALL_SPACING,
+ ) { currentCoverIndex ->
+
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ queue?.get(currentCoverIndex)?.let {
+ CustomText(
+ modifier = Modifier,
+ text = it.title,
+ size = 40.sp,
+ color = MaterialTheme.colorScheme.primary
+ )
+ }
+
+ MediumVerticalSpacer()
+
+ queue?.get(currentCoverIndex)?.let {
+ CustomText(
+ text = mainVM.getSongArtist(it).name,
+ size = 30.sp
+ )
+ }
+ }
+ }
+ }
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f)
+ .fillMaxHeight()
+ ) {
+
+ Slider(
+ value = secondsSliderValue,
+ interactionSource = secondsSliderInteractionSource,
+ onValueChange = {
+
+ secondsSliderValue = it
+ mainVM.updateCurrentSongMinutesAndSecondsText(mainVM.getMinutesAndSeconds(it.toInt()))
+ },
+ onValueChangeFinished = {
+
+ if (!musicPlaying)
mainVM.pauseResumeMusic()
mainVM.seekSongSeconds(secondsSliderValue.toInt())
@@ -1019,31 +2020,110 @@ fun LandscapePlayer(
)
}
+ MediumVerticalSpacer()
+
Row(
modifier = Modifier
- .wrapContentHeight()
- .fillMaxWidth(),
- horizontalArrangement = Arrangement.SpaceEvenly,
- verticalAlignment = Alignment.CenterVertically
- ) {
+ .fillMaxHeight()
+ .weight(1f, fill = true)
+ ){
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f, fill = true),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ){
+ Icon(
+ painter = painterResource(id = R.drawable.previous),
+ contentDescription = "Select Previous Song",
+ tint = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(5.dp)
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+ mainVM.selectPreviousSong()
+ }
+ )
+ }
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier.wrapContentHeight()
- ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f, fill = true),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ){
+ Icon(
+ bitmap = if (musicPlaying) {
+ remember { getImage(context, R.drawable.icon_pause_round_solid, ImageSizes.MEDIUM) }
+ } else {
+ remember { getImage(context, R.drawable.icon_play_round_solid, ImageSizes.MEDIUM) }
+ }.asImageBitmap(),
+ contentDescription = "Play/Pause Button",
+ tint = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier
+ .fillMaxSize()
+ .aspectRatio(1f)
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+ mainVM.pauseResumeMusic()
+ }
+ )
+ }
- if (queueShuffled) {
- Spacer(modifier = Modifier.height(XSMALL_SPACING)) //Needed to keep shuffle button in place when shuffle is enabled
- }
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f, fill = true),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ){
+ Icon(
+ painter = painterResource(id = R.drawable.next),
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier
+ .padding(5.dp)
+ .fillMaxSize()
+ .clickable(
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+
+ mainVM.selectNextSong()
+ }
+ )
+ }
+ }
+
+ Row(
+ modifier = Modifier
+ .fillMaxHeight()
+ .weight(1f, fill = true)
+ .padding(10.dp),
+ ){
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f, fill = true),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ){
Icon(
painter = painterResource(id = R.drawable.shuffle),
contentDescription = "",
tint = if (queueShuffled) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
modifier = Modifier
- .size(35.dp)
- .fillMaxSize()
- .fillMaxSize()
+ .fillMaxHeight()
+ .weight(1f, fill = true)
+ .aspectRatio(1f)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
@@ -1054,79 +2134,28 @@ fun LandscapePlayer(
)
if (queueShuffled) {
- Dot()
+ Dot(carMode = true)
+ }else{
+ Spacer(modifier = Modifier.height(8.dp))
}
}
- Icon(
- painter = painterResource(id = R.drawable.previous),
- contentDescription = "Select Previous Song",
- tint = MaterialTheme.colorScheme.onSurface,
- modifier = Modifier
- .size(35.dp)
- .fillMaxSize()
- .fillMaxSize()
- .clickable(
- indication = null,
- interactionSource = remember { MutableInteractionSource() }
- ) {
- mainVM.selectPreviousSong()
- }
- )
-
- Icon(
- painter = if (musicPlaying) {
- painterResource(id = R.drawable.icon_pause_round_solid)
- } else {
- painterResource(id = R.drawable.icon_play_round_solid)
- },
- contentDescription = "Play/Pause Button",
- tint = MaterialTheme.colorScheme.onSurface,
- modifier = Modifier
- .size(80.dp)
- .fillMaxSize()
- .clickable(
- indication = null,
- interactionSource = remember { MutableInteractionSource() }
- ) {
- mainVM.pauseResumeMusic()
- }
- )
-
- Icon(
- painter = painterResource(id = R.drawable.next),
- contentDescription = "Next Song",
- tint = MaterialTheme.colorScheme.onSurface,
- modifier = Modifier
- .size(35.dp)
- .fillMaxSize()
- .fillMaxSize()
- .clickable(
- indication = null,
- interactionSource = remember { MutableInteractionSource() }
- ) {
-
- mainVM.selectNextSong()
- }
- )
-
Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier.wrapContentHeight()
- ) {
-
- if (songOnRepeat) {
-
- Spacer(modifier = Modifier.height(XSMALL_SPACING)) //Needed to keep repeat button in place when repeat is enabled
- }
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f, fill = true),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ){
Icon(
painter = painterResource(id = R.drawable.repeat),
contentDescription = "Repeat Song",
tint = if (songOnRepeat) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
modifier = Modifier
- .size(35.dp)
- .fillMaxSize()
+ .fillMaxHeight()
+ .weight(1f, fill = true)
+ .aspectRatio(1f)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
@@ -1136,17 +2165,21 @@ fun LandscapePlayer(
)
if (songOnRepeat) {
- Dot()
+ Dot(carMode = true)
+ }else{
+ Spacer(modifier = Modifier.height(8.dp))
}
}
}
-
}
-
}
+ }
- //Queue Tab
- if (selectedPage == 1) {
+ if (selectedPage == 1) {
+
+ Column {
+
+ MediumVerticalSpacer()
if (upNextQueue!!.isEmpty()) {
@@ -1171,14 +2204,18 @@ fun LandscapePlayer(
.fillMaxSize()
.then(Modifier.reorderable(state))
) {
- items(upNextQueue, { it.id }) { song ->
+ itemsIndexed(items = upNextQueue, key = { _, song -> song.id }) { index, song ->
ReorderableItem(state, key = song.id) { isDragging ->
ReorderableSongItem(
mainVM = mainVM,
song = song,
state = state,
- isDragging = isDragging
+ isDragging = isDragging,
+ carMode = true,
+ onClick = {
+ mainVM.onUpNextQueueMove(ItemPosition(index, song.id), ItemPosition(0, upNextQueue[0].id))
+ }
)
}
}
@@ -1191,11 +2228,16 @@ fun LandscapePlayer(
}
@Composable
-fun Dot() {
+fun KeepScreenOn() = AndroidView({ View(it).apply { keepScreenOn = true } })
+
+@Composable
+fun Dot(
+ carMode: Boolean = false
+) {
Box(
modifier = Modifier
- .height(XSMALL_SPACING)
- .width(XSMALL_SPACING)
+ .height(if (carMode) 8.dp else XSMALL_SPACING)
+ .width(if (carMode) 8.dp else XSMALL_SPACING)
.clip(RoundedCornerShape(percent = 100))
.background(MaterialTheme.colorScheme.primary)
)
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/settings/SettingsScreen.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/settings/SettingsScreen.kt
index e270c27..5524ecb 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/settings/SettingsScreen.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/screens/main/settings/SettingsScreen.kt
@@ -54,6 +54,8 @@ fun SettingsScreen(
val filterAudioSetting = settingsVM.filterAudioSetting.collectAsState().value
val downloadArtistCoverSetting = settingsVM.downloadArtistCoverSetting.collectAsState().value
val downloadOverDataSetting = settingsVM.downloadOverDataSetting.collectAsState().value
+ val carPlayerSetting = settingsVM.carPlayerSetting.collectAsState().value
+ val keepScreenOnInCarModeSetting = settingsVM.keepScreenOnInCarModeSetting.collectAsState().value
val selectedThemeMode = vm.selectedColorScheme.collectAsState().value
val selectedDarkMode = vm.selectedDarkMode.collectAsState().value
val filterAudioText = vm.filterAudioText.collectAsState().value
@@ -79,8 +81,6 @@ fun SettingsScreen(
SmallVerticalSpacer()
-
-
Text(
text = remember { getAppString(context, R.string.Theming) },
color = MaterialTheme.colorScheme.primary,
@@ -118,7 +118,7 @@ fun SettingsScreen(
)
- if(isAtLeastAndroid10()){
+ if (isAtLeastAndroid10()) {
DefaultSettingItem(
icon = painterResource(id = R.drawable.brush),
settingText = remember { getAppString(context, R.string.LightColorScheme) },
@@ -132,7 +132,7 @@ fun SettingsScreen(
settingValue = darkColorScheme,
onSettingClick = { onOpenScreen(Routes.Root.DARK_COLOR_SCHEMES) }
)
- }else{
+ } else {
DefaultSettingItem(
icon = painterResource(id = R.drawable.brush),
settingText = remember { getAppString(context, R.string.ColorScheme) },
@@ -315,7 +315,6 @@ fun SettingsScreen(
MediumVerticalSpacer()
-
Text(
text = remember { getAppString(context, R.string.Audio) },
color = MaterialTheme.colorScheme.primary,
@@ -389,6 +388,49 @@ fun SettingsScreen(
)
}
+ MediumVerticalSpacer()
+
+ Text(
+ text = remember { getAppString(context, R.string.ExperimentalFeatures) },
+ color = MaterialTheme.colorScheme.primary,
+ fontSize = 22.sp,
+ textAlign = TextAlign.Start,
+ maxLines = 1,
+ modifier = Modifier
+ .fillMaxWidth()
+ .offset(8.dp)
+ )
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .wrapContentHeight()
+ .clip(RoundedCornerShape(14.dp))
+ .background(MaterialTheme.colorScheme.surfaceVariant)
+ ) {
+ SwitchSettingItem(
+ icon = painterResource(id = R.drawable.car),
+ settingText = remember { getAppString(context, R.string.CarPlayer) },
+ settingValue = carPlayerSetting,
+ onToggle = {
+
+ settingsVM.updateCarPlayerSetting(!carPlayerSetting)
+ }
+ )
+
+ SwitchSettingItem(
+ icon = painterResource(id = R.drawable.car),
+ settingText = remember { getAppString(context, R.string.KeepScreenOnInCarMode) },
+ settingValue = keepScreenOnInCarModeSetting,
+ enabled = carPlayerSetting,
+ onToggle = {
+
+ settingsVM.updateKeepScreenOnCarModeSetting(!keepScreenOnInCarModeSetting)
+ }
+ )
+
+ }
+
if (showDarkModeDialog) {
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/services/SimpleMPService.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/services/SimpleMPService.kt
index 8b8bb73..f3032ee 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/services/SimpleMPService.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/services/SimpleMPService.kt
@@ -460,6 +460,15 @@ class SimpleMPService : Service() {
} catch (exc: Exception) {
Log.e("Service Error", exc.toString())
+
+ //Removes song from queues
+ val song = getQueue()[currentSongPosition]
+
+ queueList.remove(song)
+ shuffledQueueList.remove(song)
+
+ currentSongPosition -= 1
+
selectNextSong(context)
}
}
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/settings/SettingsVM.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/settings/SettingsVM.kt
index e27480e..e7f42f9 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/settings/SettingsVM.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/settings/SettingsVM.kt
@@ -73,4 +73,18 @@ class SettingsVM(application: Application) : AndroidViewModel(application){
preferences.edit().putBoolean(Settings.DOWNLOAD_OVER_DATA, newValue).apply()
_downloadOverDataSetting.update { newValue }
}
+
+ private val _carPlayerSetting = MutableStateFlow(preferences.getBoolean(Settings.CAR_PLAYER, false))
+ val carPlayerSetting = _carPlayerSetting.asStateFlow()
+ fun updateCarPlayerSetting(newValue:Boolean) {
+ preferences.edit().putBoolean(Settings.CAR_PLAYER, newValue).apply()
+ _carPlayerSetting.update { newValue }
+ }
+
+ private val _keepScreenOnInCarModeSetting = MutableStateFlow(preferences.getBoolean(Settings.KEEP_SCREEN_ON_IN_CAR_MODE, true))
+ val keepScreenOnInCarModeSetting = _keepScreenOnInCarModeSetting.asStateFlow()
+ fun updateKeepScreenOnCarModeSetting(newValue:Boolean) {
+ preferences.edit().putBoolean(Settings.KEEP_SCREEN_ON_IN_CAR_MODE, newValue).apply()
+ _keepScreenOnInCarModeSetting.update { newValue }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/ui/composables/SongItem.kt b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/ui/composables/SongItem.kt
index 1c2f6ed..cadb252 100644
--- a/app/src/main/java/com/lighttigerxiv/simple/mp/compose/ui/composables/SongItem.kt
+++ b/app/src/main/java/com/lighttigerxiv/simple/mp/compose/ui/composables/SongItem.kt
@@ -3,6 +3,7 @@ package com.lighttigerxiv.simple.mp.compose.ui.composables
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -15,6 +16,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
@@ -174,7 +176,9 @@ fun ReorderableSongItem(
modifier: Modifier = Modifier,
song: Song,
state: ReorderableLazyListState,
- isDragging: Boolean
+ isDragging: Boolean,
+ carMode: Boolean = false,
+ onClick: () -> Unit = {}
) {
val context = LocalContext.current
@@ -187,9 +191,10 @@ fun ReorderableSongItem(
Row(
modifier = modifier
.fillMaxWidth()
- .height(70.dp)
+ .height(if(carMode) 100.dp else 70.dp)
.clip(RoundedCornerShape(14.dp))
.background(if (isDragging) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.surfaceVariant)
+ .clickable { onClick() }
) {
AsyncImage(
@@ -198,8 +203,8 @@ fun ReorderableSongItem(
colorFilter = if (art == null) ColorFilter.tint(MaterialTheme.colorScheme.primary) else null,
modifier = Modifier
.clip(RoundedCornerShape(20))
- .height(70.dp)
- .width(70.dp)
+ .height(if(carMode) 100.dp else 70.dp)
+ .width(if(carMode) 100.dp else 70.dp)
.modifyIf(art == null) {
background(surfaceColor)
}
@@ -248,11 +253,13 @@ fun ReorderableSongItem(
Image(
modifier = Modifier
- .height(24.dp)
+ .height(if(carMode) 100.dp else 70.dp)
+ .width(if(carMode) 40.dp else 30.dp)
.detectReorder(state),
painter = painterResource(id = R.drawable.drag),
contentDescription = "",
- colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface)
+ colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface),
+ contentScale = ContentScale.Fit
)
}
diff --git a/app/src/main/res/drawable/car.xml b/app/src/main/res/drawable/car.xml
new file mode 100644
index 0000000..8635635
--- /dev/null
+++ b/app/src/main/res/drawable/car.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/drawable/smartphone.xml b/app/src/main/res/drawable/smartphone.xml
new file mode 100644
index 0000000..1d10e04
--- /dev/null
+++ b/app/src/main/res/drawable/smartphone.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
new file mode 100644
index 0000000..227b61b
--- /dev/null
+++ b/app/src/main/res/values-de/strings.xml
@@ -0,0 +1,107 @@
+
+
+ SimpleMP
+ Undefiniert
+ BEISPIEL
+ Füge Widget hinzu
+ Es spielt keine Musik. Zum Öffnen klicken.
+ Suche Songs
+ Suche Künstler
+ Suche Album
+ Suche Playlists
+ nach Änderungsdatum sortieren
+ Alphabetisch sortieren
+ Sortiere nach zuletzt Hinzugefügt
+ Sortiere nach älteste Hinzugefügt
+ Sortiere Aufsteigend
+ Sortiere Absteigend
+ Einstellungen
+ Über uns
+ Appversion
+ Quelle der Anwendung
+ Quellcode auf GitHub
+ Quellcode auf GitLab
+ Start
+ Genres
+ Deine Playlisten
+ Name der Playliste
+ Füge Name der Playliste hinzu
+ Erstellen
+ Abspielen
+ Shuffle
+ Bearbeite Playlist
+ Lösche Playlist
+ Möchten Sie die Wiedergabeliste wirklich löschen?
+ Abbrechen
+ Löschen
+ Keine Songs hinzugefügt
+ Künstler
+ Titel
+ Alben
+ Titel
+ Nächster Titel
+ Zum Künstler
+ Zum Album
+ Füge zur Playlist hinzu
+ Theming
+ Farbschema
+ Dunkler Modus
+ Akzentfarbe
+ Wähle Farbschema
+ Systemvoreinstellung
+ Heller Modus
+ Anwenden
+ Audio
+ Unter Audio filtern
+ Selektiere dunklen Modus
+ Farbe
+ Audio filtern
+ Gib minimale Sekunden ein.
+ System
+ Blau
+ Rot
+ Lila
+ Orange
+ Gelb
+ Grün
+ Rosa
+ Voreingestellt
+ Herzlich Willkommen zu Simple MP
+ Weiter
+ Erteilt
+ Erteilen
+ Berechtigungen
+ Andere Einstellungen
+ Erlauben Sie die Speicherberechtigung, um alle Musikdateien auf Ihrem Gerät laden zu können.
+ Erlauben Sie die Berechtigung für Benachrichtigungen, um Benachrichtigungen zur Musik anzeigen zu können.
+ Zurück
+ Akzente
+ Fertigstellen
+ Dieses Bild ist zu groß. Bitte versuchen Sie das Bild zu verkleinern.
+ Mudar capa de artista
+ Artista
+ Künstler-Cover ändern
+ Online Ergebnisse
+ Aktiviere das Internet um Cover zu erhalten.
+ Standard verwenden
+ Daten
+ Lade Künstler-Cover über das Internet herunter.
+ Lade Künstler-Cover über mobile Daten herunter. (möglicherweise fallen Kosten an)
+ Playlists
+ Dieser Titel ist bereits in der Playlist
+ Füge Titel hinzu
+ Playlist
+ Hinzufügen
+ Auswählen
+ Bild auswählen
+ Bild löschen
+ Changelog
+ Herunterladen
+ Von GitHub Releases herunterladen
+ Von Playstore herunterladen
+ Von F-Droid herunterladen
+ Songs aktualisieren
+ Schließen Sie die App nicht, während Sie Songs geladen werden!!
+ Schließen Sie die App nicht, während Sie Songs geladen werden!!
+ Songs werden geladen
+
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 0901bc8..f3a3864 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -101,4 +101,12 @@
Recarregar músicas
Não feche a aplicação enquanto as músicas recarregam
A recarregar músicas
+ Esquema De Cores Escuro
+ Esquema De Cores Claro
+ A indexar m�sicas. N�o feche a aplicaç�o
+ Funcionalidades Experimentais
+ Player Do Carro
+ Manter Ecr� Ativo Em Modo Carro
+ Mostar Player Do Carro
+ Ordenar Por Artista
\ 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 0e77f03..359a921 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -118,4 +118,9 @@
Dark Color Scheme
Light Color Scheme
Indexing Songs. Don\'t close the app.
+ Experimental Features
+ Car Player
+ Keep Screen On In Car Mode
+ Show Car Player
+ Sort By Artist
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index cd0519b..3c7a8bd 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -20,4 +20,4 @@ kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
-android.nonTransitiveRClass=true
\ No newline at end of file
+android.nonTransitiveRClass=true