diff --git a/app/.DS_Store b/app/.DS_Store index 6902a0c..fc824dc 100644 Binary files a/app/.DS_Store and b/app/.DS_Store differ diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fcb8ca9..493f04e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,8 +13,8 @@ android { applicationId = "com.sosauce.cutemusic" minSdk = 26 targetSdk = 35 - versionCode = 16 - versionName = "2.2.5" + versionCode = 17 + versionName = "2.3.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { useSupportLibrary = true diff --git a/app/release/baselineProfiles/0/app-release.dm b/app/release/baselineProfiles/0/app-release.dm index 7149107..11ab5b4 100644 Binary files a/app/release/baselineProfiles/0/app-release.dm and b/app/release/baselineProfiles/0/app-release.dm differ diff --git a/app/release/baselineProfiles/1/app-release.dm b/app/release/baselineProfiles/1/app-release.dm index 25a3749..33f76fd 100644 Binary files a/app/release/baselineProfiles/1/app-release.dm and b/app/release/baselineProfiles/1/app-release.dm differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index c7a5ead..10124c9 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -11,8 +11,8 @@ "type": "SINGLE", "filters": [], "attributes": [], - "versionCode": 15, - "versionName": "2.2.4", + "versionCode": 16, + "versionName": "2.2.5", "outputFile": "app-release.apk" } ], diff --git a/app/src/.DS_Store b/app/src/.DS_Store index 2752949..0025842 100644 Binary files a/app/src/.DS_Store and b/app/src/.DS_Store differ diff --git a/app/src/main/.DS_Store b/app/src/main/.DS_Store index 6baf6b4..7377c7f 100644 Binary files a/app/src/main/.DS_Store and b/app/src/main/.DS_Store differ diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png index 9ed72a3..adb7763 100644 Binary files a/app/src/main/ic_launcher-playstore.png and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/com/sosauce/cutemusic/data/MusicState.kt b/app/src/main/java/com/sosauce/cutemusic/data/MusicState.kt index a7aa0b5..d6e9e29 100644 --- a/app/src/main/java/com/sosauce/cutemusic/data/MusicState.kt +++ b/app/src/main/java/com/sosauce/cutemusic/data/MusicState.kt @@ -23,4 +23,5 @@ data class MusicState( val currentSize: Long = 0, val currentLrcFile: File? = null, val playbackParameters: PlaybackParameters = PlaybackParameters.DEFAULT, + val isPlayerReady: Boolean = false ) diff --git a/app/src/main/java/com/sosauce/cutemusic/data/actions/MetadataActions.kt b/app/src/main/java/com/sosauce/cutemusic/data/actions/MetadataActions.kt index f32cf05..332040c 100644 --- a/app/src/main/java/com/sosauce/cutemusic/data/actions/MetadataActions.kt +++ b/app/src/main/java/com/sosauce/cutemusic/data/actions/MetadataActions.kt @@ -1,8 +1,11 @@ package com.sosauce.cutemusic.data.actions +import android.net.Uri + sealed interface MetadataActions { data class LoadSong( - val path: String + val path: String, + val uri: Uri ) : MetadataActions data object SaveChanges : MetadataActions diff --git a/app/src/main/java/com/sosauce/cutemusic/data/datastore/DataStore.kt b/app/src/main/java/com/sosauce/cutemusic/data/datastore/DataStore.kt index d4de856..7c6de4a 100644 --- a/app/src/main/java/com/sosauce/cutemusic/data/datastore/DataStore.kt +++ b/app/src/main/java/com/sosauce/cutemusic/data/datastore/DataStore.kt @@ -11,11 +11,11 @@ import com.sosauce.cutemusic.data.datastore.PreferencesKeys.APPLY_LOOP import com.sosauce.cutemusic.data.datastore.PreferencesKeys.BLACKLISTED_FOLDERS import com.sosauce.cutemusic.data.datastore.PreferencesKeys.FOLLOW_SYS import com.sosauce.cutemusic.data.datastore.PreferencesKeys.HAS_SEEN_TIP +import com.sosauce.cutemusic.data.datastore.PreferencesKeys.SHOW_ALBUMS_TAB +import com.sosauce.cutemusic.data.datastore.PreferencesKeys.SHOW_ARTISTS_TAB +import com.sosauce.cutemusic.data.datastore.PreferencesKeys.SHOW_FOLDERS_TAB import com.sosauce.cutemusic.data.datastore.PreferencesKeys.SHOW_X_BUTTON import com.sosauce.cutemusic.data.datastore.PreferencesKeys.SNAP_SPEED_N_PITCH -import com.sosauce.cutemusic.data.datastore.PreferencesKeys.SORT_ORDER -import com.sosauce.cutemusic.data.datastore.PreferencesKeys.SORT_ORDER_ALBUMS -import com.sosauce.cutemusic.data.datastore.PreferencesKeys.SORT_ORDER_ARTISTS import com.sosauce.cutemusic.data.datastore.PreferencesKeys.USE_AMOLED_MODE import com.sosauce.cutemusic.data.datastore.PreferencesKeys.USE_ART_THEME import com.sosauce.cutemusic.data.datastore.PreferencesKeys.USE_CLASSIC_SLIDER @@ -28,9 +28,6 @@ private const val PREFERENCES_NAME = "settings" val Context.dataStore: DataStore by preferencesDataStore(PREFERENCES_NAME) private data object PreferencesKeys { - val SORT_ORDER = booleanPreferencesKey("sort_order") - val SORT_ORDER_ARTISTS = booleanPreferencesKey("sort_order_artists") - val SORT_ORDER_ALBUMS = booleanPreferencesKey("sort_order_albums") val USE_DARK_MODE = booleanPreferencesKey("use_dark_mode") val USE_AMOLED_MODE = booleanPreferencesKey("use_amoled_mode") val FOLLOW_SYS = booleanPreferencesKey("follow_sys") @@ -43,20 +40,11 @@ private data object PreferencesKeys { val APPLY_LOOP = booleanPreferencesKey("apply_loop") val USE_CLASSIC_SLIDER = booleanPreferencesKey("use_classic_slider") val SHOW_X_BUTTON = booleanPreferencesKey("show_x_button") + val SHOW_ALBUMS_TAB = booleanPreferencesKey("show_albums_tab") + val SHOW_ARTISTS_TAB = booleanPreferencesKey("show_artists_tab") + val SHOW_FOLDERS_TAB = booleanPreferencesKey("show_folders_tab") } -@Composable -fun rememberSortASC() = - rememberPreference(key = SORT_ORDER, defaultValue = true) - -@Composable -fun rememberSortASCArtists() = - rememberPreference(key = SORT_ORDER_ARTISTS, defaultValue = true) - -@Composable -fun rememberSortASCAlbums() = - rememberPreference(key = SORT_ORDER_ALBUMS, defaultValue = true) - @Composable fun rememberUseDarkMode() = rememberPreference(key = USE_DARK_MODE, defaultValue = false) @@ -103,6 +91,18 @@ fun rememberUseClassicSlider() = fun rememberShowXButton() = rememberPreference(key = SHOW_X_BUTTON, defaultValue = true) +@Composable +fun rememberShowAlbumsTab() = + rememberPreference(key = SHOW_ALBUMS_TAB, defaultValue = true) + +@Composable +fun rememberShowArtistsTab() = + rememberPreference(key = SHOW_ARTISTS_TAB, defaultValue = true) + +@Composable +fun rememberShowFoldersTab() = + rememberPreference(key = SHOW_FOLDERS_TAB, defaultValue = true) + suspend fun getBlacklistedFolder(context: Context): Set { val preferences = context.dataStore.data.first() diff --git a/app/src/main/java/com/sosauce/cutemusic/data/datastore/SettingsExt.kt b/app/src/main/java/com/sosauce/cutemusic/data/datastore/SettingsExt.kt index 07013a8..29565d4 100644 --- a/app/src/main/java/com/sosauce/cutemusic/data/datastore/SettingsExt.kt +++ b/app/src/main/java/com/sosauce/cutemusic/data/datastore/SettingsExt.kt @@ -58,7 +58,8 @@ fun rememberPreferenceNonComposable( val coroutineScope = CoroutineScope(Dispatchers.Default) val state = context.dataStore.data - .map { it[key] ?: defaultValue }.stateIn(coroutineScope, SharingStarted.WhileSubscribed(5000), defaultValue) + .map { it[key] ?: defaultValue } + .stateIn(coroutineScope, SharingStarted.WhileSubscribed(5000), defaultValue) return object : MutableState { override var value: T diff --git a/app/src/main/java/com/sosauce/cutemusic/domain/repository/MediaStoreHelper.kt b/app/src/main/java/com/sosauce/cutemusic/domain/repository/MediaStoreHelper.kt index 478cf79..f85400e 100644 --- a/app/src/main/java/com/sosauce/cutemusic/domain/repository/MediaStoreHelper.kt +++ b/app/src/main/java/com/sosauce/cutemusic/domain/repository/MediaStoreHelper.kt @@ -13,6 +13,7 @@ interface MediaStoreHelper { val musics: List val albums: List val artists: List + val folders: List fun fetchMusics(): List diff --git a/app/src/main/java/com/sosauce/cutemusic/domain/repository/MediaStoreHelperImpl.kt b/app/src/main/java/com/sosauce/cutemusic/domain/repository/MediaStoreHelperImpl.kt index 4768df9..123f73f 100644 --- a/app/src/main/java/com/sosauce/cutemusic/domain/repository/MediaStoreHelperImpl.kt +++ b/app/src/main/java/com/sosauce/cutemusic/domain/repository/MediaStoreHelperImpl.kt @@ -26,12 +26,17 @@ class MediaStoreHelperImpl( private val context: Context ) : MediaStoreHelper { - private fun getBlacklistedFoldersAsync(): Set = runBlocking { getBlacklistedFolder(context) } + private fun getBlacklistedFoldersAsync(): Set = + runBlocking { getBlacklistedFolder(context) } + + private val blacklistedFolders = getBlacklistedFoldersAsync() + private val selection = + blacklistedFolders.joinToString(" AND ") { "${MediaStore.Audio.Media.DATA} NOT LIKE ?" } + private val selectionArgs = blacklistedFolders.map { "$it%" }.toTypedArray() @UnstableApi override fun fetchMusics(): List { - var blacklistedFolders = getBlacklistedFoldersAsync() val musics = mutableListOf() val projection = arrayOf( @@ -43,14 +48,9 @@ class MediaStoreHelperImpl( MediaStore.Audio.Media.ALBUM_ID, MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.SIZE, - MediaStore.Audio.Media.DURATION, - //MediaStore.Audio.Media.IS_FAVORITE, + MediaStore.Audio.Media.DURATION ) - val selection = blacklistedFolders.joinToString(" AND ") { "${MediaStore.Audio.Media.DATA} NOT LIKE ?" } - val selectionArgs = blacklistedFolders.map { "$it%" }.toTypedArray() - - context.contentResolver.query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, @@ -153,7 +153,9 @@ class MediaStoreHelperImpl( val albumInfo = Album(id, album, artist) if (albums.none { it.name == albumInfo.name }) { - albums.add(albumInfo) + if (musics.map { it.mediaMetadata.extras?.getLong("album_id") }.contains(id)) { + albums.add(albumInfo) + } } } } @@ -187,7 +189,9 @@ class MediaStoreHelperImpl( id = id, name = artist ) - artists.add(artistInfo) + if (musics.map { it.mediaMetadata.extras?.getLong("artist_id") }.contains(id)) { + artists.add(artistInfo) + } } } @@ -275,4 +279,5 @@ class MediaStoreHelperImpl( override val musics: List = fetchMusics() override val albums: List = fetchAlbums() override val artists: List = fetchArtists() + override val folders: List = fetchFoldersWithMusics() } diff --git a/app/src/main/java/com/sosauce/cutemusic/main/PlaybackService.kt b/app/src/main/java/com/sosauce/cutemusic/main/PlaybackService.kt index 352ec28..e3b7e10 100644 --- a/app/src/main/java/com/sosauce/cutemusic/main/PlaybackService.kt +++ b/app/src/main/java/com/sosauce/cutemusic/main/PlaybackService.kt @@ -95,7 +95,7 @@ class PlaybackService : MediaLibraryService(), .build() setMediaNotificationProvider( DefaultMediaNotificationProvider.Builder(this).build().apply { - setSmallIcon(R.drawable.round_music_note_24) + setSmallIcon(R.drawable.music_note_rounded) } ) diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/navigation/Navigation.kt b/app/src/main/java/com/sosauce/cutemusic/ui/navigation/Navigation.kt index 7470de0..3a61f7d 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/navigation/Navigation.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/navigation/Navigation.kt @@ -13,9 +13,9 @@ import androidx.navigation.compose.rememberNavController import androidx.navigation.toRoute import com.sosauce.cutemusic.data.actions.MetadataActions import com.sosauce.cutemusic.data.actions.PlayerActions -import com.sosauce.cutemusic.data.datastore.rememberAllBlacklistedFolders import com.sosauce.cutemusic.ui.screens.album.AlbumDetailsScreen import com.sosauce.cutemusic.ui.screens.album.AlbumsScreen +import com.sosauce.cutemusic.ui.screens.all_folders.AllFoldersScreen import com.sosauce.cutemusic.ui.screens.artist.ArtistDetails import com.sosauce.cutemusic.ui.screens.artist.ArtistsScreen import com.sosauce.cutemusic.ui.screens.blacklisted.BlacklistedScreen @@ -39,25 +39,21 @@ fun Nav() { val viewModel = koinViewModel() val postViewModel = koinViewModel() val metadataViewModel = koinViewModel() - val blacklistedFolders by rememberAllBlacklistedFolders() val musics = postViewModel.musics val musicState by viewModel.musicState.collectAsStateWithLifecycle() + val folders = postViewModel.folders SharedTransitionLayout { - - this NavHost( navController = navController, startDestination = Screen.Main ) { - - this@SharedTransitionLayout composable { MainScreen( musics = musics, selectedIndex = viewModel.selectedItem, - onNavigateTo = { navController.navigate(it) }, + onNavigate = { navController.navigate(it) }, currentlyPlaying = musicState.currentlyPlaying, isCurrentlyPlaying = musicState.isCurrentlyPlaying, onShortClick = { viewModel.handlePlayerActions(PlayerActions.StartPlayback(it)) }, @@ -68,15 +64,16 @@ fun Nav() { } }, animatedVisibilityScope = this, - onLoadMetadata = { uri -> + onLoadMetadata = { path, uri -> metadataViewModel.onHandleMetadataActions(MetadataActions.ClearState) metadataViewModel.onHandleMetadataActions( MetadataActions.LoadSong( + path, uri ) ) }, - isPlayerReady = viewModel.isPlayerReady(), + isPlayerReady = musicState.isPlayerReady, currentMusicUri = musicState.currentMusicUri, onHandlePlayerAction = { viewModel.handlePlayerActions(it) }, onDeleteMusic = { uris, intentSender -> @@ -124,7 +121,7 @@ fun Nav() { }, currentlyPlaying = musicState.currentlyPlaying, chargePVMAlbumSongs = postViewModel::albumSongs, - isPlayerReady = viewModel.isPlayerReady(), + isPlayerReady = musicState.isPlayerReady, isPlaying = musicState.isCurrentlyPlaying, onHandlePlayerActions = viewModel::handlePlayerActions, onNavigate = { navController.navigate(it) }, @@ -157,7 +154,7 @@ fun Nav() { onHandlePlayerActions = viewModel::handlePlayerActions, isPlaying = musicState.isCurrentlyPlaying, animatedVisibilityScope = this, - isPlayerReady = viewModel.isPlayerReady(), + isPlayerReady = musicState.isPlayerReady, onHandleSorting = { sortingType -> postViewModel.handleFiltering( listToHandle = ListToHandle.ARTISTS, @@ -245,6 +242,25 @@ fun Nav() { ) } } + + composable { + AllFoldersScreen( + musics = musics, + onNavigationItemClicked = { index, item -> + navController.navigate(item.navigateTo) { + viewModel.selectedItem = index + launchSingleTop = true + } + }, + selectedIndex = viewModel.selectedItem, + onNavigate = { navController.navigate(it) }, + currentlyPlaying = musicState.currentlyPlaying, + isCurrentlyPlaying = musicState.isCurrentlyPlaying, + onHandlePlayerActions = viewModel::handlePlayerActions, + isPlayerReady = musicState.isPlayerReady, + animatedVisibilityScope = this, + ) + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/navigation/Screen.kt b/app/src/main/java/com/sosauce/cutemusic/ui/navigation/Screen.kt index a49f1cd..a4c083d 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/navigation/Screen.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/navigation/Screen.kt @@ -22,6 +22,9 @@ sealed class Screen { @Serializable data object Artists : Screen() + @Serializable + data object AllFolders : Screen() + @Serializable data class AlbumsDetails( val id: Long diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/album/AlbumDetailsLandscape.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/album/AlbumDetailsLandscape.kt index a41164e..4e3e275 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/album/AlbumDetailsLandscape.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/album/AlbumDetailsLandscape.kt @@ -111,7 +111,7 @@ fun AlbumDetailsLandscape( ) ) }, - isPlayerReady = viewModel.isPlayerReady() + isPlayerReady = musicState.isPlayerReady ) } } diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/album/AlbumDetailsScreen.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/album/AlbumDetailsScreen.kt index 8ce2b63..b999915 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/album/AlbumDetailsScreen.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/album/AlbumDetailsScreen.kt @@ -184,7 +184,7 @@ private fun SharedTransitionScope.AlbumDetailsContent( ) }, currentMusicUri = musicState.currentMusicUri, - isPlayerReady = viewModel.isPlayerReady() + isPlayerReady = musicState.isPlayerReady ) } } @@ -193,7 +193,7 @@ private fun SharedTransitionScope.AlbumDetailsContent( CuteSearchbar( currentlyPlaying = musicState.currentlyPlaying, - isPlayerReady = viewModel.isPlayerReady(), + isPlayerReady = musicState.isPlayerReady, isPlaying = musicState.isCurrentlyPlaying, onHandlePlayerActions = viewModel::handlePlayerActions, animatedVisibilityScope = animatedVisibilityScope, diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/album/AlbumScreen.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/album/AlbumScreen.kt index 13d3c40..39491ee 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/album/AlbumScreen.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/album/AlbumScreen.kt @@ -26,7 +26,6 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Album import androidx.compose.material.icons.rounded.ArrowUpward import androidx.compose.material.icons.rounded.Settings import androidx.compose.material3.DropdownMenu @@ -43,6 +42,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -160,7 +160,7 @@ fun SharedTransitionScope.AlbumsScreen( leadingIcon = { IconButton(onClick = { screenSelectionExpanded = true }) { Icon( - imageVector = Icons.Rounded.Album, + painter = painterResource(androidx.media3.session.R.drawable.media3_icon_album), contentDescription = null ) } diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/all_folders/AllFoldersScreen.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/all_folders/AllFoldersScreen.kt new file mode 100644 index 0000000..dff4fe6 --- /dev/null +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/all_folders/AllFoldersScreen.kt @@ -0,0 +1,255 @@ +@file:OptIn(ExperimentalSharedTransitionApi::class) + +package com.sosauce.cutemusic.ui.screens.all_folders + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.AnimatedVisibilityScope +import androidx.compose.animation.ExperimentalSharedTransitionApi +import androidx.compose.animation.SharedTransitionScope +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.animateFloatAsState +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.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.ArrowBackIosNew +import androidx.compose.material.icons.rounded.ArrowUpward +import androidx.compose.material.icons.rounded.Settings +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateMapOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.media3.common.MediaItem +import com.sosauce.cutemusic.R +import com.sosauce.cutemusic.data.actions.PlayerActions +import com.sosauce.cutemusic.domain.model.Folder +import com.sosauce.cutemusic.ui.navigation.Screen +import com.sosauce.cutemusic.ui.screens.blacklisted.components.FolderItem +import com.sosauce.cutemusic.ui.screens.main.MusicListItem +import com.sosauce.cutemusic.ui.shared_components.CuteSearchbar +import com.sosauce.cutemusic.ui.shared_components.CuteText +import com.sosauce.cutemusic.ui.shared_components.NavigationItem +import com.sosauce.cutemusic.ui.shared_components.ScreenSelection +import com.sosauce.cutemusic.utils.rememberSearchbarAlignment +import com.sosauce.cutemusic.utils.rememberSearchbarMaxFloatValue +import com.sosauce.cutemusic.utils.rememberSearchbarRightPadding + +@Composable +fun SharedTransitionScope.AllFoldersScreen( + musics: List, + onNavigationItemClicked: (Int, NavigationItem) -> Unit, + selectedIndex: Int, + onNavigate: (Screen) -> Unit, + currentlyPlaying: String, + isCurrentlyPlaying: Boolean, + onHandlePlayerActions: (PlayerActions) -> Unit, + isPlayerReady: Boolean, + animatedVisibilityScope: AnimatedVisibilityScope, +) { + + var query by remember { mutableStateOf("") } + val groupedMusics = remember(musics) { + musics.groupBy { + it.mediaMetadata.extras?.getString("path") + ?.substring(0, it.mediaMetadata.extras?.getString("path")?.lastIndexOf('/') ?: 0) + } + } + val groupedAndFilteredMusics = remember(query, musics) { + if (query.isEmpty()) { + groupedMusics + } else groupedMusics.filter { it.key?.contains(query, true) != false } + + } + var areMusicsVisible = remember { mutableStateMapOf() } + var screenSelectionExpanded by remember { mutableStateOf(false) } + var isSortedByASC by remember { mutableStateOf(true) } // I prolly should change this + val float by animateFloatAsState( + targetValue = if (isSortedByASC) 45f else 135f, + label = "Arrow Icon Animation" + ) + + Box(Modifier.fillMaxSize()) { + + LazyColumn( + modifier = Modifier + .fillMaxSize() + .statusBarsPadding() + ) { + + groupedAndFilteredMusics.onEachIndexed { index, (folder, musics) -> + item( + key = folder + ) { + val isExpanded = areMusicsVisible[folder] == true + + val rotation by animateFloatAsState( + targetValue = if (isExpanded) 270f else 180f, + label = "Arrow Rotation" + ) + val bottomDp by animateDpAsState( + targetValue = if (index == groupedAndFilteredMusics.keys.size - 1 || isExpanded) 24.dp else 4.dp, + label = "" + ) + val topDp by animateDpAsState( + targetValue = if (index == 0 || isExpanded) 24.dp else 4.dp, + label = "" + ) + + FolderItem( + folder = Folder( + name = folder?.substring(folder.lastIndexOf('/') + 1) ?: "No Name", + path = folder.toString() + ), + onClick = { areMusicsVisible[folder ?: "No Name"] = !isExpanded }, + topDp = topDp, + bottomDp = bottomDp, + icon = { + Icon( + imageVector = Icons.Rounded.ArrowBackIosNew, + contentDescription = null, + modifier = Modifier + .size(30.dp) + .rotate(rotation), + tint = MaterialTheme.colorScheme.onBackground + ) + } + ) + } + + items( + items = musics, + key = { it.mediaId } + ) { music -> + AnimatedVisibility(areMusicsVisible[folder] == true) { + Column( + modifier = Modifier + .animateItem() + .padding( + start = 20.dp + ) + ) { + MusicListItem( + onShortClick = { }, + music = music, + currentMusicUri = "", + showBottomSheet = false, + isPlayerReady = true, + ) + } + } + } + + } + } + + CuteSearchbar( + query = query, + onQueryChange = { query = it }, + modifier = Modifier + .navigationBarsPadding() + .fillMaxWidth(rememberSearchbarMaxFloatValue()) + .padding( + bottom = 5.dp, + end = rememberSearchbarRightPadding() + ) + .align(rememberSearchbarAlignment()), + placeholder = { + CuteText( + text = stringResource(id = R.string.search) + " " + stringResource( + id = R.string.folders + ), + color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.5f), + + ) + }, + leadingIcon = { + IconButton( + onClick = { + screenSelectionExpanded = true + } + ) { + Icon( + painter = painterResource(R.drawable.folder_rounded), + contentDescription = null + ) + } + + + DropdownMenu( + expanded = screenSelectionExpanded, + onDismissRequest = { screenSelectionExpanded = false }, + modifier = Modifier + .width(180.dp) + .background(color = MaterialTheme.colorScheme.surface), + shape = RoundedCornerShape(24.dp) + ) { + ScreenSelection( + onNavigationItemClicked = onNavigationItemClicked, + selectedIndex = selectedIndex + ) + } + }, + trailingIcon = { + Row { + IconButton( + onClick = { + isSortedByASC = !isSortedByASC + when (isSortedByASC) { + true -> { /* sort by ASC */ + } + + false -> { /* sort by DESC */ + } + } + } + ) { + Icon( + imageVector = Icons.Rounded.ArrowUpward, + contentDescription = null, + modifier = Modifier.rotate(float) + ) + } + IconButton( + onClick = { onNavigate(Screen.Settings) } + ) { + Icon( + imageVector = Icons.Rounded.Settings, + contentDescription = null + ) + } + } + }, + currentlyPlaying = currentlyPlaying, + onHandlePlayerActions = onHandlePlayerActions, + isPlaying = isCurrentlyPlaying, + animatedVisibilityScope = animatedVisibilityScope, + isPlayerReady = isPlayerReady, + onNavigate = { onNavigate(Screen.NowPlaying) }, + onClickFAB = { onHandlePlayerActions(PlayerActions.PlayRandom) } + ) + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/artist/ArtistDetails.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/artist/ArtistDetails.kt index 99991ac..01893ad 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/artist/ArtistDetails.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/artist/ArtistDetails.kt @@ -15,10 +15,11 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api @@ -27,6 +28,7 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -65,6 +67,7 @@ fun SharedTransitionScope.ArtistDetails( val artistSongs by remember { mutableStateOf(postViewModel.artistSongs) } val artistAlbums by remember { mutableStateOf(postViewModel.artistAlbums) } + val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() if (rememberIsLandscape()) { ArtistDetailsLandscape( @@ -76,10 +79,11 @@ fun SharedTransitionScope.ArtistDetails( chargePVMAlbumSongs = { postViewModel.albumSongs(it) }, artist = artist, currentMusicUri = musicState.currentMusicUri, - isPlayerReady = viewModel.isPlayerReady() + isPlayerReady = musicState.isPlayerReady ) } else { Scaffold( + //modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { TopAppBar( title = { @@ -106,7 +110,8 @@ fun SharedTransitionScope.ArtistDetails( contentDescription = "Back arrow" ) } - } + }, + scrollBehavior = scrollBehavior ) } ) { values -> @@ -120,7 +125,9 @@ fun SharedTransitionScope.ArtistDetails( bottom = values.calculateBottomPadding() ) ) { - Column { + Column( + modifier = Modifier.verticalScroll(rememberScrollState()) + ) { LazyRow { items(items = artistAlbums, key = { it.id }) { album -> AlbumCard( @@ -139,27 +146,25 @@ fun SharedTransitionScope.ArtistDetails( HorizontalDivider() } Spacer(modifier = Modifier.height(10.dp)) - LazyColumn { - items(artistSongs) { music -> - MusicListItem( - music = music, - onShortClick = { - viewModel.handlePlayerActions( - PlayerActions.StartArtistPlayback( - artistName = artist.name, - mediaId = it - ) + artistSongs.forEach { music -> + MusicListItem( + music = music, + onShortClick = { + viewModel.handlePlayerActions( + PlayerActions.StartArtistPlayback( + artistName = artist.name, + mediaId = it ) - }, - currentMusicUri = musicState.currentMusicUri, - isPlayerReady = viewModel.isPlayerReady() - ) - } + ) + }, + currentMusicUri = musicState.currentMusicUri, + isPlayerReady = musicState.isPlayerReady + ) } } CuteSearchbar( currentlyPlaying = musicState.currentlyPlaying, - isPlayerReady = viewModel.isPlayerReady(), + isPlayerReady = musicState.isPlayerReady, isPlaying = musicState.isCurrentlyPlaying, onHandlePlayerActions = viewModel::handlePlayerActions, animatedVisibilityScope = animatedVisibilityScope, diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/artist/ArtistsScreen.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/artist/ArtistsScreen.kt index 283776b..e858f33 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/artist/ArtistsScreen.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/artist/ArtistsScreen.kt @@ -23,7 +23,6 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ArrowUpward -import androidx.compose.material.icons.rounded.Person import androidx.compose.material.icons.rounded.Settings import androidx.compose.material3.DropdownMenu import androidx.compose.material3.Icon @@ -37,13 +36,13 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import coil3.compose.AsyncImage import com.sosauce.cutemusic.R import com.sosauce.cutemusic.data.MusicState import com.sosauce.cutemusic.data.actions.PlayerActions @@ -53,7 +52,6 @@ import com.sosauce.cutemusic.ui.shared_components.CuteSearchbar import com.sosauce.cutemusic.ui.shared_components.CuteText import com.sosauce.cutemusic.ui.shared_components.NavigationItem import com.sosauce.cutemusic.ui.shared_components.ScreenSelection -import com.sosauce.cutemusic.utils.ImageUtils import com.sosauce.cutemusic.utils.SortingType import com.sosauce.cutemusic.utils.rememberSearchbarAlignment import com.sosauce.cutemusic.utils.rememberSearchbarMaxFloatValue @@ -150,7 +148,7 @@ fun SharedTransitionScope.ArtistsScreen( leadingIcon = { IconButton(onClick = { screenSelectionExpanded = true }) { Icon( - imageVector = Icons.Rounded.Person, + painter = painterResource(R.drawable.artist_rounded), contentDescription = null ) } @@ -220,7 +218,6 @@ fun ArtistInfoList( artist: Artist, onClick: () -> Unit, ) { - val context = LocalContext.current Row( modifier = Modifier .fillMaxWidth() @@ -229,17 +226,20 @@ fun ArtistInfoList( ) { Row(verticalAlignment = Alignment.CenterVertically) { - AsyncImage( - model = ImageUtils.imageRequester( - img = R.drawable.artist, - context = context - ), - contentDescription = stringResource(id = R.string.artwork), + Box( modifier = Modifier .padding(start = 10.dp) - .size(45.dp), - contentScale = ContentScale.Crop, - ) + .size(50.dp) + .clip(RoundedCornerShape(12.dp)) + .background(Color(0xFFFAB3AA)), + contentAlignment = Alignment.Center + ) { + Icon( + painter = painterResource(R.drawable.artist_rounded), + contentDescription = stringResource(id = R.string.artwork), + modifier = Modifier.size(30.dp) + ) + } Column( modifier = Modifier.padding(20.dp) diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/blacklisted/components/AllFolderBottomSheet.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/blacklisted/components/AllFolderBottomSheet.kt index 1d5c0b5..8b4704d 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/blacklisted/components/AllFolderBottomSheet.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/blacklisted/components/AllFolderBottomSheet.kt @@ -1,6 +1,5 @@ package com.sosauce.cutemusic.ui.screens.blacklisted.components -import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -11,14 +10,14 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.FolderOpen +import androidx.compose.material.icons.rounded.Folder import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.sosauce.cutemusic.domain.model.Folder @@ -35,13 +34,21 @@ fun AllFoldersBottomSheet( items = folders, key = { _, folder -> folder.path } ) { index, folder -> - FoldersLayout( + FolderItem( folder = folder, onClick = { path -> onClick(path) }, topDp = if (index == 0) 24.dp else 4.dp, - bottomDp = if (index == folders.size - 1) 24.dp else 4.dp + bottomDp = if (index == folders.size - 1) 24.dp else 4.dp, + icon = { + Icon( + imageVector = Icons.Rounded.Folder, + contentDescription = null, + modifier = Modifier.size(33.dp), + tint = MaterialTheme.colorScheme.onBackground + ) + } ) } } @@ -49,11 +56,12 @@ fun AllFoldersBottomSheet( @Composable -private fun FoldersLayout( +fun FolderItem( folder: Folder, onClick: (path: String) -> Unit, topDp: Dp, bottomDp: Dp, + icon: @Composable () -> Unit ) { Card( colors = CardDefaults.cardColors( @@ -61,7 +69,7 @@ private fun FoldersLayout( alpha = 0.5f ) ), - modifier = Modifier.padding(5.dp), + modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp), shape = RoundedCornerShape( topStart = topDp, topEnd = topDp, @@ -73,16 +81,11 @@ private fun FoldersLayout( Row( modifier = Modifier .fillMaxWidth() - .padding(15.dp), + .padding(10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { - Image( - imageVector = Icons.Default.FolderOpen, - contentDescription = null, - modifier = Modifier.size(33.dp), - colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground) - ) + icon() Column( verticalArrangement = Arrangement.Center, modifier = Modifier diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/lyrics/LyricsView.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/lyrics/LyricsView.kt index 7c8f13e..c37fa8c 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/lyrics/LyricsView.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/lyrics/LyricsView.kt @@ -28,6 +28,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -58,15 +59,13 @@ fun LyricsView( ) { var currentLyric by remember { mutableStateOf(Lyrics()) } val clipboardManager = LocalClipboardManager.current - val indexToScrollTo = musicState.currentLyrics.indexOfFirst { lyric -> - musicState.currentPosition in lyric.timestamp until (musicState.currentLyrics.getOrNull( - musicState.currentLyrics.indexOf(lyric) + 1 - )?.timestamp ?: 0) - } val lazyListState = rememberLazyListState( - initialFirstVisibleItemIndex = if (indexToScrollTo != -1) indexToScrollTo else 0 + initialFirstVisibleItemIndex = if (musicState.currentLyrics.indexOf(currentLyric) != -1) musicState.currentLyrics.indexOf( + currentLyric + ) else 0 ) val context = LocalContext.current + val a = context.contentResolver DisposableEffect(Unit) { val window = (context as MainActivity).window @@ -77,6 +76,14 @@ fun LyricsView( } } + LaunchedEffect(currentLyric) { + val indexOfCurrentLyric = musicState.currentLyrics.indexOf(currentLyric) + + if (indexOfCurrentLyric != -1) { + lazyListState.animateScrollToItem(musicState.currentLyrics.indexOf(currentLyric)) + } + } + Box( @@ -103,8 +110,7 @@ fun LyricsView( key = { _, item -> item.timestamp } ) { index, lyric -> - - val nextTimestamp = remember(index, musicState.currentLyrics) { + val nextTimestamp = remember(index) { if (index < musicState.currentLyrics.size - 1) { musicState.currentLyrics[index + 1].timestamp } else { @@ -112,10 +118,13 @@ fun LyricsView( } } - val isCurrentLyric = remember(musicState.currentPosition, nextTimestamp) { - musicState.currentPosition in lyric.timestamp until nextTimestamp + val isCurrentLyric by remember(musicState.currentPosition) { + derivedStateOf { + musicState.currentPosition in lyric.timestamp until nextTimestamp + } } + val color by animateColorAsState( targetValue = if (isCurrentLyric) { MaterialTheme.colorScheme.onBackground @@ -127,9 +136,6 @@ fun LyricsView( if (isCurrentLyric) { currentLyric = lyric - LaunchedEffect(Unit) { - lazyListState.animateScrollToItem(index) - } } diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/main/MainScreen.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/main/MainScreen.kt index 3961cf0..da1f7bf 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/main/MainScreen.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/main/MainScreen.kt @@ -44,16 +44,10 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Album import androidx.compose.material.icons.rounded.ArrowUpward -import androidx.compose.material.icons.rounded.Delete -import androidx.compose.material.icons.rounded.Edit -import androidx.compose.material.icons.rounded.Info +import androidx.compose.material.icons.rounded.ErrorOutline import androidx.compose.material.icons.rounded.MoreVert -import androidx.compose.material.icons.rounded.MusicNote -import androidx.compose.material.icons.rounded.Person import androidx.compose.material.icons.rounded.Settings -import androidx.compose.material.icons.rounded.Share import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem @@ -75,6 +69,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -103,12 +98,12 @@ fun SharedTransitionScope.MainScreen( musics: List, currentlyPlaying: String, isCurrentlyPlaying: Boolean, - onNavigateTo: (Screen) -> Unit, + onNavigate: (Screen) -> Unit, onShortClick: (String) -> Unit, onNavigationItemClicked: (Int, NavigationItem) -> Unit, selectedIndex: Int, animatedVisibilityScope: AnimatedVisibilityScope, - onLoadMetadata: (String) -> Unit = {}, + onLoadMetadata: (String, Uri) -> Unit = { _, _ -> }, isPlayerReady: Boolean, currentMusicUri: String, onHandlePlayerAction: (PlayerActions) -> Unit, @@ -133,7 +128,7 @@ fun SharedTransitionScope.MainScreen( true } else if ( - // Are both the first and last element visible ? + // Are both the first and last element visible ? state.layoutInfo.visibleItemsInfo.firstOrNull()?.index == 0 && state.layoutInfo.visibleItemsInfo.lastOrNull()?.index == musics.size - 1 ) { @@ -177,7 +172,7 @@ fun SharedTransitionScope.MainScreen( MusicListItem( onShortClick = { onShortClick(music.mediaId) }, music = music, - onNavigate = { onNavigateTo(it) }, + onNavigate = { onNavigate(it) }, currentMusicUri = currentMusicUri, onLoadMetadata = onLoadMetadata, showBottomSheet = true, @@ -247,7 +242,7 @@ fun SharedTransitionScope.MainScreen( } ) { Icon( - imageVector = Icons.Rounded.MusicNote, + painter = painterResource(R.drawable.music_note_rounded), contentDescription = null, tint = if (!hasSeenTip) color else LocalContentColor.current ) @@ -291,7 +286,7 @@ fun SharedTransitionScope.MainScreen( ) } IconButton( - onClick = { onNavigateTo(Screen.Settings) } + onClick = { onNavigate(Screen.Settings) } ) { Icon( imageVector = Icons.Rounded.Settings, @@ -305,7 +300,7 @@ fun SharedTransitionScope.MainScreen( isPlaying = isCurrentlyPlaying, animatedVisibilityScope = animatedVisibilityScope, isPlayerReady = isPlayerReady, - onNavigate = { onNavigateTo(Screen.NowPlaying) }, + onNavigate = { onNavigate(Screen.NowPlaying) }, onClickFAB = { onHandlePlayerAction(PlayerActions.PlayRandom) } ) } @@ -321,7 +316,7 @@ fun MusicListItem( onShortClick: (albumName: String) -> Unit, onNavigate: (Screen) -> Unit = {}, currentMusicUri: String, - onLoadMetadata: (String) -> Unit = {}, + onLoadMetadata: (String, Uri) -> Unit = { _, _ -> }, showBottomSheet: Boolean = false, onDeleteMusic: (List, ActivityResultLauncher) -> Unit = { _, _ -> }, onChargeAlbumSongs: (String) -> Unit = {}, @@ -363,13 +358,6 @@ fun MusicListItem( } } - val shareIntent = Intent() - .apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_STREAM, uri) - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - type = "audio/*" - } if (showDetailsDialog) { MusicDetailsDialog( @@ -455,27 +443,28 @@ fun MusicListItem( }, leadingIcon = { Icon( - imageVector = Icons.Rounded.Info, - contentDescription = null - ) - } - ) - DropdownMenuItem( - onClick = { - isDropDownExpanded = false - onLoadMetadata(path ?: "") - onNavigate(Screen.MetadataEditor(music.mediaId)) - }, - text = { - CuteText(stringResource(R.string.edit)) - }, - leadingIcon = { - Icon( - imageVector = Icons.Rounded.Edit, - contentDescription = null + imageVector = Icons.Rounded.ErrorOutline, + contentDescription = null, + modifier = Modifier.rotate(180f) ) } ) +// DropdownMenuItem( +// onClick = { +// isDropDownExpanded = false +// onLoadMetadata(path ?: "", uri) +// onNavigate(Screen.MetadataEditor(music.mediaId)) +// }, +// text = { +// CuteText(stringResource(R.string.edit)) +// }, +// leadingIcon = { +// Icon( +// painter = painterResource(R.drawable.edit_rounded), +// contentDescription = null +// ) +// } +// ) DropdownMenuItem( onClick = { isDropDownExpanded = false @@ -491,7 +480,7 @@ fun MusicListItem( }, leadingIcon = { Icon( - imageVector = Icons.Rounded.Album, + painter = painterResource(androidx.media3.session.R.drawable.media3_icon_album), contentDescription = null ) } @@ -511,13 +500,20 @@ fun MusicListItem( }, leadingIcon = { Icon( - imageVector = Icons.Rounded.Person, + painter = painterResource(R.drawable.artist_rounded), contentDescription = null ) } ) DropdownMenuItem( onClick = { + val shareIntent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_STREAM, uri) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + type = "audio/*" + } + context.startActivity( Intent.createChooser( shareIntent, @@ -532,7 +528,7 @@ fun MusicListItem( }, leadingIcon = { Icon( - imageVector = Icons.Rounded.Share, + painter = painterResource(androidx.media3.session.R.drawable.media3_icon_share), contentDescription = null ) } @@ -547,7 +543,7 @@ fun MusicListItem( }, leadingIcon = { Icon( - imageVector = Icons.Rounded.Delete, + painter = painterResource(R.drawable.trash_rounded), contentDescription = null, tint = MaterialTheme.colorScheme.error ) diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/main/components/ShareOptionsContent.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/main/components/ShareOptionsContent.kt index 4e81f31..a3d8b90 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/main/components/ShareOptionsContent.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/main/components/ShareOptionsContent.kt @@ -7,7 +7,6 @@ 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 @@ -19,9 +18,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import com.sosauce.cutemusic.R import com.sosauce.cutemusic.ui.shared_components.CuteText @@ -83,12 +80,12 @@ fun ShareOptionsContent() { horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { - Icon( - painter = painterResource(id = R.drawable.ribbon), - contentDescription = null, - modifier = Modifier - .size(24.dp) - ) +// Icon( +// painter = painterResource(id = R.drawable.ribbon), +// contentDescription = null, +// modifier = Modifier +// .size(24.dp) +// ) Spacer(Modifier.width(10.dp)) CuteText("CuteShare") } diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/metadata/MetadataEditor.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/metadata/MetadataEditor.kt index 2597bb6..b9132d5 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/metadata/MetadataEditor.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/metadata/MetadataEditor.kt @@ -2,6 +2,7 @@ package com.sosauce.cutemusic.ui.screens.metadata import android.app.Activity import android.net.Uri +import android.util.Log import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.ActivityResultLauncher @@ -21,7 +22,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Done +import androidx.compose.material.icons.rounded.Done import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.OutlinedTextField @@ -92,6 +93,8 @@ fun MetadataEditorContent( rememberLauncherForActivityResult( contract = ActivityResultContracts.StartIntentSenderForResult() ) { + Log.d("resulta", it.resultCode.toString()) + if (it.resultCode == Activity.RESULT_OK) { onMetadataAction(MetadataActions.SaveChanges) Toast.makeText( @@ -121,7 +124,7 @@ fun MetadataEditorContent( } ) { Icon( - imageVector = Icons.Default.Done, + imageVector = Icons.Rounded.Done, contentDescription = null ) } diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/metadata/MetadataState.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/metadata/MetadataState.kt index 1780942..af95e10 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/metadata/MetadataState.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/metadata/MetadataState.kt @@ -1,10 +1,12 @@ package com.sosauce.cutemusic.ui.screens.metadata +import android.net.Uri import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.snapshots.SnapshotStateList data class MetadataState( val mutablePropertiesMap: SnapshotStateList = mutableStateListOf(), - val songPath: String = "" + val songPath: String = "", + val songUri: Uri = Uri.EMPTY //var art: Artwork? = null ) \ No newline at end of file diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/metadata/MetadataViewModel.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/metadata/MetadataViewModel.kt index 0f7c9e6..8a83888 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/metadata/MetadataViewModel.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/metadata/MetadataViewModel.kt @@ -1,7 +1,11 @@ package com.sosauce.cutemusic.ui.screens.metadata +import android.annotation.SuppressLint import android.app.Application -import android.media.MediaScannerConnection +import android.content.Context +import android.net.Uri +import android.os.ParcelFileDescriptor +import android.provider.MediaStore import android.util.Log import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope @@ -9,9 +13,7 @@ import com.sosauce.cutemusic.data.actions.MetadataActions import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch -import org.jaudiotagger.audio.AudioFileIO -import org.jaudiotagger.tag.FieldKey -import java.io.File +import java.io.FileNotFoundException class MetadataViewModel( private val application: Application @@ -28,85 +30,81 @@ class MetadataViewModel( private fun loadMetadataJAudio(path: String) { - val audioFile = AudioFileIO - .read(File(path)) - - audioFile.tag.apply { - val tagList = listOf( - getFirst(FieldKey.TITLE), - getFirst(FieldKey.ARTIST), - getFirst(FieldKey.ALBUM), - getFirst(FieldKey.YEAR), - getFirst(FieldKey.GENRE), - getFirst(FieldKey.TRACK), - getFirst(FieldKey.DISC_NO), - getFirst(FieldKey.LYRICS), - ) +// val audioFile = AudioFileIO +// .read(File(path)) +// +// audioFile.tag.apply { +// val tagList = listOf( +// getFirst(FieldKey.TITLE), +// getFirst(FieldKey.ARTIST), +// getFirst(FieldKey.ALBUM), +// getFirst(FieldKey.YEAR), +// getFirst(FieldKey.GENRE), +// getFirst(FieldKey.TRACK), +// getFirst(FieldKey.DISC_NO), +// getFirst(FieldKey.LYRICS), +// ) +// +// +// tagList.forEach { +// _metadata.value.mutablePropertiesMap.add(it) +// } +// //_metadata.value.art = firstArtwork ?: null +// } + } - tagList.forEach { - _metadata.value.mutablePropertiesMap.add(it) - } - //_metadata.value.art = firstArtwork ?: null - } + private fun saveAllChanges() { } - private fun saveAllChanges(path: String) { - try { - val file = File(path) - val audioFile = AudioFileIO.read(file) - - audioFile.tag.apply { - mapOf( - FieldKey.TITLE to 0, - FieldKey.ARTIST to 1, - FieldKey.ALBUM to 2, - FieldKey.YEAR to 3, - FieldKey.GENRE to 4, - FieldKey.TRACK to 5, - FieldKey.DISC_NO to 6, - FieldKey.LYRICS to 7 - ) - .forEach { - Log.d("Test", _metadata.value.mutablePropertiesMap[it.value]) - setField(it.key, _metadata.value.mutablePropertiesMap[it.value]) + private fun clearState() { + _metadata.value.mutablePropertiesMap.clear() + //_metadata.value.art + } + + @SuppressLint("Range") + private fun getFileDescriptorFromPath( + context: Context, + filePath: String, + mode: String = "r" + ): ParcelFileDescriptor? { + val resolver = context.contentResolver + val uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + + val projection = arrayOf(MediaStore.Files.FileColumns._ID) + val selection = "${MediaStore.Files.FileColumns.DATA}=?" + val selectionArgs = arrayOf(filePath) + + resolver.query(uri, projection, selection, selectionArgs, null)?.use { cursor -> + if (cursor.moveToFirst()) { + val fileId = cursor.getInt(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID)) + if (fileId == -1) { + return null + } else { + val fileUri = Uri.withAppendedPath(uri, fileId.toString()) + try { + return resolver.openFileDescriptor(fileUri, mode) + } catch (e: FileNotFoundException) { + Log.e("MediaStoreReceiver", "File not found: ${e.message}") } + } } - - AudioFileIO.write(audioFile) - - MediaScannerConnection.scanFile( - application.applicationContext, - arrayOf(file.toString()), - null, - null - ) - - } catch (e: Exception) { - Log.d("CuteError", e.message.toString()) } - } - - private fun clearState() { - _metadata.value.mutablePropertiesMap.clear() - //_metadata.value.art + return null } fun onHandleMetadataActions(action: MetadataActions) { when (action) { - is MetadataActions.SaveChanges -> { - viewModelScope.launch { - saveAllChanges(metadataState.value.songPath) - } - } + is MetadataActions.SaveChanges -> saveAllChanges() is MetadataActions.LoadSong -> { viewModelScope.launch { _metadata.value = _metadata.value.copy( - songPath = action.path + songPath = action.path, + songUri = action.uri ) loadMetadataJAudio(metadataState.value.songPath) } @@ -121,4 +119,3 @@ class MetadataViewModel( - diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/playing/components/QuickActionsRow.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/playing/components/QuickActionsRow.kt index 7181b7c..860a9e1 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/playing/components/QuickActionsRow.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/playing/components/QuickActionsRow.kt @@ -8,12 +8,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.rounded.Article -import androidx.compose.material.icons.rounded.Album -import androidx.compose.material.icons.rounded.Info +import androidx.compose.material.icons.rounded.ErrorOutline import androidx.compose.material.icons.rounded.MoreVert -import androidx.compose.material.icons.rounded.Person -import androidx.compose.material.icons.rounded.Share import androidx.compose.material.icons.rounded.Speed import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem @@ -26,7 +22,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.sosauce.cutemusic.R @@ -74,7 +72,7 @@ fun QuickActionsRow( IconButton(onClick = onShowLyrics) { Icon( - imageVector = Icons.AutoMirrored.Rounded.Article, + painter = painterResource(R.drawable.lyrics_rounded), contentDescription = "show lyrics" ) } @@ -106,8 +104,9 @@ fun QuickActionsRow( }, leadingIcon = { Icon( - imageVector = Icons.Rounded.Info, - contentDescription = null + imageVector = Icons.Rounded.ErrorOutline, + contentDescription = null, + modifier = Modifier.rotate(180f) ) } ) @@ -122,7 +121,7 @@ fun QuickActionsRow( }, leadingIcon = { Icon( - imageVector = Icons.Rounded.Album, + painter = painterResource(androidx.media3.session.R.drawable.media3_icon_album), contentDescription = null ) } @@ -138,7 +137,7 @@ fun QuickActionsRow( }, leadingIcon = { Icon( - imageVector = Icons.Rounded.Person, + painter = painterResource(R.drawable.artist_rounded), contentDescription = null ) } @@ -152,7 +151,7 @@ fun QuickActionsRow( }, leadingIcon = { Icon( - imageVector = Icons.Rounded.Share, + painter = painterResource(androidx.media3.session.R.drawable.media3_icon_share), contentDescription = null ) } diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/SettingsScreen.kt index 0f9ea8c..9ae277c 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/SettingsScreen.kt @@ -55,7 +55,7 @@ fun SettingsScreen( Spacer(Modifier.height(10.dp)) ThemeManagement() UISettings() - Misc(onNavigateTo = onNavigate) + Misc(onNavigate = onNavigate) // SettingCategoryCards( // text = "UI & Theme", // onClick = {}, diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/compenents/AboutCard.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/compenents/AboutCard.kt index d7702c5..40ce0f2 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/compenents/AboutCard.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/compenents/AboutCard.kt @@ -1,6 +1,7 @@ package com.sosauce.cutemusic.ui.screens.settings.compenents -import androidx.compose.foundation.Image +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.fillMaxWidth @@ -10,11 +11,13 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource @@ -40,14 +43,28 @@ fun AboutCard() { Row( verticalAlignment = Alignment.CenterVertically ) { - Image( - painterResource(id = R.drawable.cute_music_icon), - contentDescription = stringResource(id = R.string.app_icon), +// Image( +// painterResource(id = R.drawable.cute_music_icon), +// contentDescription = stringResource(id = R.string.app_icon), +// modifier = Modifier +// .size(100.dp) +// .padding(15.dp) +// .clip(RoundedCornerShape(15)) +// ) + Box( modifier = Modifier .size(100.dp) .padding(15.dp) .clip(RoundedCornerShape(15)) - ) + .background(Color(0xFFFAB3AA)), + contentAlignment = Alignment.Center + ) { + Icon( + painter = painterResource(R.drawable.music_note_rounded), + contentDescription = stringResource(id = R.string.app_icon), + modifier = Modifier.size(60.dp) + ) + } Column { CuteText( text = stringResource(id = R.string.cm_by_sosauce), diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/compenents/Switches.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/compenents/Switches.kt index 0797e35..f896254 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/compenents/Switches.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/compenents/Switches.kt @@ -12,6 +12,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -19,6 +21,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.sosauce.cutemusic.R import com.sosauce.cutemusic.data.datastore.rememberFollowSys +import com.sosauce.cutemusic.data.datastore.rememberShowAlbumsTab +import com.sosauce.cutemusic.data.datastore.rememberShowArtistsTab +import com.sosauce.cutemusic.data.datastore.rememberShowFoldersTab import com.sosauce.cutemusic.data.datastore.rememberShowXButton import com.sosauce.cutemusic.data.datastore.rememberUseAmoledMode import com.sosauce.cutemusic.data.datastore.rememberUseClassicSlider @@ -30,7 +35,7 @@ import com.sosauce.cutemusic.utils.restart @Composable fun Misc( - onNavigateTo: (Screen) -> Unit + onNavigate: (Screen) -> Unit ) { val context = LocalContext.current //var killService by remember { rememberKillService(context) } @@ -43,7 +48,7 @@ fun Misc( ) TextSettingsCards( text = stringResource(id = R.string.blacklisted_folders), - onClick = { onNavigateTo(Screen.Blacklisted) }, + onClick = { onNavigate(Screen.Blacklisted) }, modifier = Modifier .padding( top = 25.dp, @@ -125,6 +130,20 @@ fun UISettings() { var useClassicSlider by rememberUseClassicSlider() var useSystemFont by rememberUseSystemFont() var showXButton by rememberShowXButton() + var showAlbumsTab by rememberShowAlbumsTab() + var showArtistsTab by rememberShowArtistsTab() + var showFoldersTab by rememberShowFoldersTab() + + var showTabManager by remember { mutableStateOf(false) } + + if (showTabManager) { + TabManager( + onDismissRequest = { showTabManager = false }, + showArtistsTab = showArtistsTab, + showAlbumsTab = showAlbumsTab, + showFoldersTab = showFoldersTab + ) + } Column { CuteText( @@ -132,10 +151,23 @@ fun UISettings() { color = MaterialTheme.colorScheme.primary, modifier = Modifier.padding(horizontal = 34.dp, vertical = 8.dp) ) + TextSettingsCards( + text = stringResource(id = R.string.manage_shown_tabs), + onClick = { showTabManager = true }, + modifier = Modifier + .padding( + top = 25.dp, + start = 15.dp, + bottom = 25.dp + ) + .fillMaxWidth(), + topDp = 24.dp, + bottomDp = 4.dp + ) SettingsCards( checked = useClassicSlider, onCheckedChange = { useClassicSlider = !useClassicSlider }, - topDp = 24.dp, + topDp = 4.dp, bottomDp = 4.dp, text = stringResource(id = R.string.classic_slider), ) diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/compenents/TabManager.kt b/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/compenents/TabManager.kt new file mode 100644 index 0000000..7df0bcc --- /dev/null +++ b/app/src/main/java/com/sosauce/cutemusic/ui/screens/settings/compenents/TabManager.kt @@ -0,0 +1,76 @@ +package com.sosauce.cutemusic.ui.screens.settings.compenents + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Checkbox +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.res.stringResource +import com.sosauce.cutemusic.R +import com.sosauce.cutemusic.data.datastore.rememberShowAlbumsTab +import com.sosauce.cutemusic.data.datastore.rememberShowArtistsTab +import com.sosauce.cutemusic.data.datastore.rememberShowFoldersTab +import com.sosauce.cutemusic.ui.shared_components.CuteText + +@Composable +fun TabManager( + onDismissRequest: () -> Unit, + showAlbumsTab: Boolean, + showArtistsTab: Boolean, + showFoldersTab: Boolean +) { + + + // These are here cuz I'm lazy having to create 3 parameters + var showAlbumsTab2 by rememberShowAlbumsTab() + var showArtistsTab2 by rememberShowArtistsTab() + var showFoldersTab2 by rememberShowFoldersTab() + + val tabsAndShownState = remember(showAlbumsTab, showFoldersTab, showArtistsTab) { + mapOf( + "Albums" to showAlbumsTab, + "Artists" to showArtistsTab, + "Folders" to showFoldersTab, + ) + } + + AlertDialog( + onDismissRequest = onDismissRequest, + confirmButton = { + TextButton( + onClick = onDismissRequest + ) { + CuteText(stringResource(R.string.okay)) + } + }, + text = { + Column { + tabsAndShownState.forEach { (tabName, isShown) -> + + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Checkbox( + checked = isShown, + onCheckedChange = { + when (tabName) { + "Albums" -> showAlbumsTab2 = !showAlbumsTab + "Artists" -> showArtistsTab2 = !showArtistsTab + "Folders" -> showFoldersTab2 = !showFoldersTab + } + } + ) + CuteText(tabName) + } + + } + } + }, + title = { CuteText(stringResource(R.string.manage_shown_tabs)) } + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/MusicViewModel.kt b/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/MusicViewModel.kt index d2397bb..11918b1 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/MusicViewModel.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/MusicViewModel.kt @@ -126,6 +126,29 @@ class MusicViewModel( } } } + + override fun onPlaybackStateChanged(playbackState: Int) { + super.onPlaybackStateChanged(playbackState) + when (playbackState) { + Player.STATE_IDLE -> { + _musicState.value = _musicState.value.copy( + isPlayerReady = false + ) + } + + Player.STATE_READY -> { + _musicState.value = _musicState.value.copy( + isPlayerReady = true + ) + } + + else -> { + _musicState.value = _musicState.value.copy( + isPlayerReady = true + ) + } + } + } } init { @@ -204,16 +227,6 @@ class MusicViewModel( mediaController!!.release() } - - fun isPlayerReady(): Boolean { - return if (mediaController == null) false else - when (mediaController!!.playbackState) { - Player.STATE_IDLE -> false - Player.STATE_READY -> true - else -> true - } - } - fun handlePlayerActions(action: PlayerActions) { when (action) { is PlayerActions.RestartSong -> mediaController!!.seekTo(0) diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/PostViewModel.kt b/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/PostViewModel.kt index 9bac6f2..9cd783f 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/PostViewModel.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/PostViewModel.kt @@ -38,7 +38,7 @@ class PostViewModel( ) var folders by mutableStateOf( - mediaStoreHelper.fetchFoldersWithMusics() + mediaStoreHelper.folders ) private val observer = MediaStoreObserver { diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/RadioButtons.kt b/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/RadioButtons.kt index 49f9a79..9afddbe 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/RadioButtons.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/RadioButtons.kt @@ -9,25 +9,23 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Album -import androidx.compose.material.icons.outlined.MusicNote -import androidx.compose.material.icons.outlined.Person -import androidx.compose.material.icons.rounded.Album -import androidx.compose.material.icons.rounded.MusicNote -import androidx.compose.material.icons.rounded.Person import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.sosauce.cutemusic.R +import com.sosauce.cutemusic.data.datastore.rememberShowAlbumsTab +import com.sosauce.cutemusic.data.datastore.rememberShowArtistsTab +import com.sosauce.cutemusic.data.datastore.rememberShowFoldersTab import com.sosauce.cutemusic.ui.navigation.Screen @Composable @@ -37,28 +35,48 @@ fun ScreenSelection( ) { val context = LocalContext.current + val showAlbumsTab by rememberShowAlbumsTab() + val showArtistsTab by rememberShowArtistsTab() + val showFoldersTab by rememberShowFoldersTab() - val items = listOf( - NavigationItem( - title = context.getString(R.string.music), - navigateTo = Screen.Main, - activeIcon = Icons.Rounded.MusicNote, - notActiveIcon = Icons.Outlined.MusicNote - ), - NavigationItem( - title = stringResource(R.string.albums), - navigateTo = Screen.Albums, - activeIcon = Icons.Rounded.Album, - notActiveIcon = Icons.Outlined.Album - ), - NavigationItem( - title = stringResource(R.string.artists), - navigateTo = Screen.Artists, - activeIcon = Icons.Rounded.Person, - notActiveIcon = Icons.Outlined.Person + + val items = mutableListOf().apply { + add( + NavigationItem( + title = context.getString(R.string.music), + navigateTo = Screen.Main, + icon = painterResource(R.drawable.music_note_rounded) + ) ) + if (showAlbumsTab) { + add( + NavigationItem( + title = stringResource(R.string.albums), + navigateTo = Screen.Albums, + icon = painterResource(androidx.media3.session.R.drawable.media3_icon_album) + ) + ) + } + if (showArtistsTab) { + add( + NavigationItem( + title = stringResource(R.string.artists), + navigateTo = Screen.Artists, + icon = painterResource(R.drawable.artist_rounded) + ) + ) + } + if (showFoldersTab) { + add( + NavigationItem( + title = stringResource(R.string.folders), + navigateTo = Screen.AllFolders, + icon = painterResource(R.drawable.folder_rounded) + ) + ) + } + } - ) Column( @@ -85,7 +103,7 @@ fun ScreenSelection( verticalAlignment = Alignment.CenterVertically ) { Icon( - imageVector = navigationItem.activeIcon, + painter = navigationItem.icon, contentDescription = navigationItem.title, modifier = Modifier.padding(start = 15.dp) ) @@ -103,6 +121,5 @@ fun ScreenSelection( data class NavigationItem( val title: String, val navigateTo: Screen, - val activeIcon: ImageVector, - val notActiveIcon: ImageVector, + val icon: Painter ) \ No newline at end of file diff --git a/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/Searchbar.kt b/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/Searchbar.kt index 3c0a1cc..5e4f027 100644 --- a/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/Searchbar.kt +++ b/app/src/main/java/com/sosauce/cutemusic/ui/shared_components/Searchbar.kt @@ -92,7 +92,8 @@ fun SharedTransitionScope.CuteSearchbar( minWidth = 45.dp, minHeight = 45.dp ) - .align(Alignment.End) + .align(Alignment.End), + shape = RoundedCornerShape(14.dp) ) { Icon( imageVector = Icons.Rounded.Shuffle, diff --git a/app/src/main/java/com/sosauce/cutemusic/utils/Customs.kt b/app/src/main/java/com/sosauce/cutemusic/utils/Customs.kt index 8710c9b..d4608ad 100644 --- a/app/src/main/java/com/sosauce/cutemusic/utils/Customs.kt +++ b/app/src/main/java/com/sosauce/cutemusic/utils/Customs.kt @@ -33,4 +33,9 @@ fun CuteIconButton( ), contentAlignment = Alignment.Center ) { content() } +} + +@Composable +inline fun Map.ComposeForEach(action: (Map.Entry) -> Unit): Unit { + for (element in this) action(element) } \ No newline at end of file diff --git a/app/src/main/res/.DS_Store b/app/src/main/res/.DS_Store index 41c7bcd..14ed3a4 100644 Binary files a/app/src/main/res/.DS_Store and b/app/src/main/res/.DS_Store differ diff --git a/app/src/main/res/drawable/artist.jpg b/app/src/main/res/drawable/artist.jpg deleted file mode 100644 index 987b6f8..0000000 Binary files a/app/src/main/res/drawable/artist.jpg and /dev/null differ diff --git a/app/src/main/res/drawable/artist_rounded.xml b/app/src/main/res/drawable/artist_rounded.xml new file mode 100644 index 0000000..7132776 --- /dev/null +++ b/app/src/main/res/drawable/artist_rounded.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/cute_music_icon.jpg b/app/src/main/res/drawable/cute_music_icon.jpg deleted file mode 100644 index 0516fbb..0000000 Binary files a/app/src/main/res/drawable/cute_music_icon.jpg and /dev/null differ diff --git a/app/src/main/res/drawable/edit_rounded.xml b/app/src/main/res/drawable/edit_rounded.xml new file mode 100644 index 0000000..4d1d11f --- /dev/null +++ b/app/src/main/res/drawable/edit_rounded.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/folder_rounded.xml b/app/src/main/res/drawable/folder_rounded.xml new file mode 100644 index 0000000..a1e978b --- /dev/null +++ b/app/src/main/res/drawable/folder_rounded.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml index 05eff97..5828e9a 100644 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,16 +1,15 @@ - - - + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?attr/colorControlNormal"> + + + diff --git a/app/src/main/res/drawable/icon_splash.xml b/app/src/main/res/drawable/icon_splash.xml new file mode 100644 index 0000000..c613892 --- /dev/null +++ b/app/src/main/res/drawable/icon_splash.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/lyrics_rounded.xml b/app/src/main/res/drawable/lyrics_rounded.xml new file mode 100644 index 0000000..bc95cd0 --- /dev/null +++ b/app/src/main/res/drawable/lyrics_rounded.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/music_note_rounded.xml b/app/src/main/res/drawable/music_note_rounded.xml new file mode 100644 index 0000000..563ff77 --- /dev/null +++ b/app/src/main/res/drawable/music_note_rounded.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ribbon.xml b/app/src/main/res/drawable/ribbon.xml deleted file mode 100644 index 037c3bd..0000000 --- a/app/src/main/res/drawable/ribbon.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/round_music_note_24.xml b/app/src/main/res/drawable/round_music_note_24.xml deleted file mode 100644 index c1fd5db..0000000 --- a/app/src/main/res/drawable/round_music_note_24.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/round_pause_24.xml b/app/src/main/res/drawable/round_pause_24.xml deleted file mode 100644 index 66f513b..0000000 --- a/app/src/main/res/drawable/round_pause_24.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/round_play_arrow_24.xml b/app/src/main/res/drawable/round_play_arrow_24.xml deleted file mode 100644 index 7e16914..0000000 --- a/app/src/main/res/drawable/round_play_arrow_24.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/trash_rounded.xml b/app/src/main/res/drawable/trash_rounded.xml new file mode 100644 index 0000000..f744b77 --- /dev/null +++ b/app/src/main/res/drawable/trash_rounded.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index a8a0db7..7005cb8 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,7 +1,6 @@ - - + + - \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index a8a0db7..7005cb8 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,7 +1,6 @@ - - + + - \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp index 7c82e95..011a709 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp index 9557016..a63a424 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp index 42f8340..a057b6d 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp index dc1037f..6f376dd 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp index d4fa4dc..7875008 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp index f9f6fd4..ed2d42d 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp index a46d049..34374d1 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp index 4544958..ad4dc00 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp index ca28767..07dadbb 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp index 323f50d..167beaa 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values-v31/colors.xml b/app/src/main/res/values-v31/colors.xml new file mode 100644 index 0000000..1023df2 --- /dev/null +++ b/app/src/main/res/values-v31/colors.xml @@ -0,0 +1,5 @@ + + + @android:color/system_neutral2_900 + @android:color/system_accent1_100 + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..0110c56 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,5 @@ + + + #201A1A + #f0d2ce + \ No newline at end of file diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml index a938753..58d2ba2 100644 --- a/app/src/main/res/values/ic_launcher_background.xml +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -1,4 +1,4 @@ - #FAB3A8 + #FAB3AA \ 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 7c61ad0..b745ff9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -72,4 +72,6 @@ Share Show Close Button on CuteSearchbar Duration + Folders + Manage Shown Tabs \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index d0eb74a..683e5a1 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -4,10 +4,8 @@ - - #201A1A \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c3178c4..5775c8b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,20 +4,20 @@ jaudiotagger = "3.0.1" koinAndroid = "4.0.0" koinAndroidxCompose = "4.0.0" koinAndroidxStartup = "4.0.0" -kotlin = "2.0.20" +kotlin = "2.0.21" activityCompose = "1.9.3" -coilCompose = "3.0.0-rc02" -composeBom = "2024.10.01" +coilCompose = "3.0.3" +composeBom = "2024.11.00" composeAnimation = "1.7.5" coreKtx = "1.15.0" coreSplashscreen = "1.0.1" datastorePreferences = "1.1.1" kotlinxSerializationJson = "1.7.3" lifecycleViewmodelCompose = "2.8.7" -media3Common = "1.4.1" -media3Exoplayer = "1.4.1" -media3Session = "1.4.1" -navigationCompose = "2.8.3" +media3Common = "1.5.0" +media3Exoplayer = "1.5.0" +media3Session = "1.5.0" +navigationCompose = "2.8.4" squigglyslider = "1.0.0" serialization = "2.0.0"