From 26f1e17b7c4b04788090fea2eec8a8e4e00fe5df Mon Sep 17 00:00:00 2001 From: Anthony La Date: Thu, 14 Nov 2024 22:18:23 -0800 Subject: [PATCH] Refactored album view and album logic --- .../anthonyla/paperize/core/WallpaperUtil.kt | 101 +++-- .../data/data_source/AlbumDatabase.kt | 2 +- .../data/repository/AlbumRepositoryImpl.kt | 16 +- .../feature/wallpaper/domain/model/Album.kt | 2 - .../wallpaper/presentation/MainActivity.kt | 34 +- .../wallpaper/presentation/PaperizeApp.kt | 120 +++--- .../add_album_screen/AddAlbumScreen.kt | 10 +- .../add_album_screen/AddAlbumViewModel.kt | 49 +-- .../components/AddAlbumSmallTopBar.kt | 6 +- .../presentation/album/AlbumsEvent.kt | 18 +- .../presentation/album/AlbumsState.kt | 5 +- .../presentation/album/AlbumsViewModel.kt | 200 ++++------ .../album/components/AlbumItem.kt | 11 +- .../album/components/FolderItem.kt | 3 +- .../album/components/WallpaperItem.kt | 14 +- .../album_view_screen/AlbumScreenViewModel.kt | 315 ++++++++++++++++ .../album_view_screen/AlbumViewEvent.kt | 38 +- .../album_view_screen/AlbumViewScreen.kt | 258 +++++++------ .../AlbumViewScreenViewModel.kt | 225 ----------- .../album_view_screen/AlbumViewState.kt | 12 +- .../components/AlbumViewTopBar.kt | 88 +++-- .../sort_view_screen/SortViewModel.kt | 5 +- .../sort_view_screen/SortViewScreen.kt | 15 +- .../wallpaper_screen/WallpaperEvent.kt | 9 - .../wallpaper_screen/WallpaperScreen.kt | 349 +++++++++--------- .../WallpaperScreenViewModel.kt | 59 --- .../wallpaper_screen/WallpaperState.kt | 8 - .../components/AlbumBottomSheet.kt | 96 ++--- .../components/CurrentSelectedAlbum.kt | 267 ++++---------- .../WallpaperViewScreen.kt | 3 +- app/src/main/res/values/strings.xml | 1 + 31 files changed, 1105 insertions(+), 1234 deletions(-) create mode 100644 app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumScreenViewModel.kt delete mode 100644 app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewScreenViewModel.kt delete mode 100644 app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperEvent.kt delete mode 100644 app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperScreenViewModel.kt delete mode 100644 app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperState.kt diff --git a/app/src/main/java/com/anthonyla/paperize/core/WallpaperUtil.kt b/app/src/main/java/com/anthonyla/paperize/core/WallpaperUtil.kt index 02962b87..86b871b4 100644 --- a/app/src/main/java/com/anthonyla/paperize/core/WallpaperUtil.kt +++ b/app/src/main/java/com/anthonyla/paperize/core/WallpaperUtil.kt @@ -1,5 +1,4 @@ package com.anthonyla.paperize.core -import android.R.attr.order import android.content.Context import android.content.res.Configuration import android.content.res.Resources @@ -18,6 +17,7 @@ import android.graphics.Shader import android.net.Uri import android.os.Build import android.provider.DocumentsContract +import android.util.Base64 import android.util.DisplayMetrics import android.util.Log import android.util.Size @@ -36,6 +36,11 @@ import com.google.android.renderscript.Toolkit import com.lazygeniouz.dfc.file.DocumentFileCompat import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.nio.charset.Charset +import java.util.zip.DeflaterOutputStream +import java.util.zip.InflaterInputStream enum class Type { HOME, LOCK, SINGLE, REFRESH } @@ -395,7 +400,7 @@ suspend fun getWallpaperFromFolder(folderUri: String, context: Context): List, folders: List): String? { - wallpapers.forEach { wallpaper -> - try { - val file = DocumentFileCompat.fromSingleUri(context, wallpaper.wallpaperUri.toUri()) - if (file?.exists() == true) { - return wallpaper.wallpaperUri - } - } catch (_: Exception) { - val file = DocumentFile.fromSingleUri(context, wallpaper.wallpaperUri.toUri()) - if (file?.exists() == true) { - return wallpaper.wallpaperUri - } - } - } +suspend fun findFirstValidUri( + context: Context, + folders: List, + wallpapers: List +): String? = withContext(Dispatchers.IO) { + // Check folder wallpapers first folders.forEach { folder -> folder.wallpapers.forEach { wallpaper -> - try { - val file = DocumentFileCompat.fromSingleUri(context, wallpaper.wallpaperUri.toUri()) - if (file?.exists() == true) { - return wallpaper.wallpaperUri - } - } catch (_: Exception) { - val file = DocumentFile.fromSingleUri(context, wallpaper.wallpaperUri.toUri()) - if (file?.exists() == true) { - return wallpaper.wallpaperUri - } - } + isValidUri(context, wallpaper.wallpaperUri).let { return@withContext wallpaper.wallpaperUri } } } - return null + // Then check individual wallpapers + wallpapers.forEach { wallpaper -> + isValidUri(context, wallpaper.wallpaperUri).let { return@withContext wallpaper.wallpaperUri } + } + return@withContext null } /** * Get the folder name from the folder URI */ -fun getFolderNameFromUri(folderUri: String, context: Context): String? { - return try { +suspend fun getFolderNameFromUri(folderUri: String, context: Context): String? = withContext(Dispatchers.IO) { + return@withContext try { DocumentFileCompat.fromTreeUri(context, folderUri.toUri())?.name } catch (_: Exception) { DocumentFile.fromTreeUri(context, folderUri.toUri())?.name @@ -507,14 +497,51 @@ fun getFolderNameFromUri(folderUri: String, context: Context): String? { * Check if a URI is valid */ fun isValidUri(context: Context, uriString: String?): Boolean { + val uri = uriString?.decompress("content://com.android.externalstorage.documents/")?.toUri() + if (uri == null) { return false } + return try { + DocumentFileCompat.fromSingleUri(context, uri)?.exists() ?: false + } catch (_: Exception) { + DocumentFile.fromSingleUri(context, uri)?.exists() ?: false + } +} + +fun isDirectory(context: Context, uriString: String?): Boolean { val uri = uriString?.toUri() + if (uri == null) { return false } return try { - uri?.let { - val inputStream = context.contentResolver.openInputStream(it) - inputStream?.close() + DocumentFileCompat.fromSingleUri(context, uri)?.isDirectory() ?: false + } catch (_: Exception) { + DocumentFile.fromSingleUri(context, uri)?.isDirectory ?: false + } +} + +/** + * Compress a string + */ +fun String.compress(prefixToRemove: String, charset: Charset = Charsets.UTF_8): String { + val modifiedInput = if (this.startsWith(prefixToRemove)) { + this.removePrefix(prefixToRemove) + } else { this } + ByteArrayOutputStream().use { byteStream -> + DeflaterOutputStream(byteStream).use { deflaterStream -> + deflaterStream.write(modifiedInput.toByteArray(charset)) + } + return Base64.encodeToString(byteStream.toByteArray(), Base64.DEFAULT) + } +} + +/** + * Decompress a string + */ +fun String.decompress(prefixToAdd: String, charset: Charset = Charsets.UTF_8): String { + val compressedData = Base64.decode(this, Base64.DEFAULT) + ByteArrayInputStream(compressedData).use { byteStream -> + InflaterInputStream(byteStream).use { inflaterStream -> + val decompressedBytes = inflaterStream.readBytes() + return prefixToAdd + String(decompressedBytes, charset) } - true - } catch (_: Exception) { false } + } } /** diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/data/data_source/AlbumDatabase.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/data/data_source/AlbumDatabase.kt index 6f0d09a3..fdbcf99b 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/data/data_source/AlbumDatabase.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/data/data_source/AlbumDatabase.kt @@ -13,7 +13,7 @@ import com.anthonyla.paperize.feature.wallpaper.domain.model.Wallpaper */ @Database( entities = [Album::class, Wallpaper::class, Folder::class], - version = 9 + version = 10 ) @TypeConverters(Converters::class) abstract class AlbumDatabase: RoomDatabase() { diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/data/repository/AlbumRepositoryImpl.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/data/repository/AlbumRepositoryImpl.kt index 6967f72f..fb3353ed 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/data/repository/AlbumRepositoryImpl.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/data/repository/AlbumRepositoryImpl.kt @@ -28,14 +28,6 @@ class AlbumRepositoryImpl( } } - override suspend fun upsertAlbumWithWallpaperAndFolder(albumWithWallpaperAndFolder: AlbumWithWallpaperAndFolder) { - withContext(dispatcher) { - dao.upsertAlbum(albumWithWallpaperAndFolder.album) - dao.upsertWallpaperList(albumWithWallpaperAndFolder.wallpapers) - dao.upsertFolderList(albumWithWallpaperAndFolder.folders) - } - } - override fun getSelectedAlbums(): Flow> { return dao.getSelectedAlbums().map { albumWithWallpaperAndFolderList -> albumWithWallpaperAndFolderList.map { @@ -48,6 +40,14 @@ class AlbumRepositoryImpl( } } + override suspend fun upsertAlbumWithWallpaperAndFolder(albumWithWallpaperAndFolder: AlbumWithWallpaperAndFolder) { + withContext(dispatcher) { + dao.upsertAlbum(albumWithWallpaperAndFolder.album) + dao.upsertWallpaperList(albumWithWallpaperAndFolder.wallpapers) + dao.upsertFolderList(albumWithWallpaperAndFolder.folders) + } + } + override suspend fun updateAlbumSelection(albumName: String, selected: Boolean) { withContext(dispatcher) { dao.updateAlbumSelection(albumName, selected) diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/domain/model/Album.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/domain/model/Album.kt index 8dfb4b07..075c9fb0 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/domain/model/Album.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/domain/model/Album.kt @@ -9,7 +9,6 @@ import androidx.room.PrimaryKey * @param initialAlbumName The initial album name -- should not be changed as it is used as the key for database queries * @param displayedAlbumName The displayed album name * @param coverUri The cover uri of the album - * @param initialized Whether the album has been initialized * @param homeWallpapersInQueue The list of home wallpapers in the queue * @param lockWallpapersInQueue The list of lock wallpapers in the queue * @param selected Whether the album is selected @@ -19,7 +18,6 @@ data class Album( @PrimaryKey(autoGenerate = false) val initialAlbumName: String, val displayedAlbumName: String, val coverUri: String?, - val initialized: Boolean = false, val homeWallpapersInQueue: List = emptyList(), val lockWallpapersInQueue: List = emptyList(), val selected : Boolean = false diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/MainActivity.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/MainActivity.kt index 7db39237..b04027bb 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/MainActivity.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/MainActivity.kt @@ -34,14 +34,10 @@ import androidx.lifecycle.lifecycleScope import com.anthonyla.paperize.core.SettingsConstants import com.anthonyla.paperize.core.Type import com.anthonyla.paperize.data.settings.SettingsDataStore -import com.anthonyla.paperize.feature.wallpaper.presentation.album.AlbumsEvent -import com.anthonyla.paperize.feature.wallpaper.presentation.album.AlbumsViewModel import com.anthonyla.paperize.feature.wallpaper.presentation.settings_screen.SettingsEvent import com.anthonyla.paperize.feature.wallpaper.presentation.settings_screen.SettingsState import com.anthonyla.paperize.feature.wallpaper.presentation.settings_screen.SettingsViewModel import com.anthonyla.paperize.feature.wallpaper.presentation.themes.PaperizeTheme -import com.anthonyla.paperize.feature.wallpaper.presentation.wallpaper_screen.WallpaperEvent -import com.anthonyla.paperize.feature.wallpaper.presentation.wallpaper_screen.WallpaperScreenViewModel import com.anthonyla.paperize.feature.wallpaper.wallpaper_alarmmanager.WallpaperAlarmItem import com.anthonyla.paperize.feature.wallpaper.wallpaper_alarmmanager.WallpaperAlarmSchedulerImpl import com.anthonyla.paperize.feature.wallpaper.wallpaper_alarmmanager.WallpaperReceiver @@ -53,11 +49,9 @@ import javax.inject.Inject @AndroidEntryPoint class MainActivity : ComponentActivity() { - private val albumsViewModel: AlbumsViewModel by viewModels() + @Inject lateinit var settingsDataStoreImpl: SettingsDataStore private val settingsViewModel: SettingsViewModel by viewModels() - private val wallpaperScreenViewModel: WallpaperScreenViewModel by viewModels() private val context = this - @Inject lateinit var settingsDataStoreImpl: SettingsDataStore override fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge( statusBarStyle = SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT), @@ -84,9 +78,6 @@ class MainActivity : ComponentActivity() { val settingsState = settingsViewModel.state.collectAsStateWithLifecycle() val isFirstLaunch = runBlocking { settingsDataStoreImpl.getBoolean(SettingsConstants.FIRST_LAUNCH) } ?: true val scheduler = WallpaperAlarmSchedulerImpl(context) - if (isFirstLaunch) { - handleFirstLaunch(scheduler) - } LifecycleEventEffect(Lifecycle.Event.ON_CREATE) { lifecycleScope.launch { @@ -109,44 +100,22 @@ class MainActivity : ComponentActivity() { } } - private fun handleFirstLaunch(scheduler: WallpaperAlarmSchedulerImpl) { - scheduler.cancelWallpaperAlarm() - wallpaperScreenViewModel.onEvent(WallpaperEvent.Reset) - settingsViewModel.onEvent(SettingsEvent.Reset) - albumsViewModel.onEvent(AlbumsEvent.Reset) - clearPersistedUriPermissions() - } - - private fun clearPersistedUriPermissions() { - val contentResolver = context.contentResolver - val persistedUris = contentResolver.persistedUriPermissions - for (permission in persistedUris) { - contentResolver.releasePersistableUriPermission( - permission.uri, - Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION - ) - } - } - private fun handleWallpaperScheduling( settings: SettingsState, scheduler: WallpaperAlarmSchedulerImpl ) { val wallpaperSettings = settings.wallpaperSettings val scheduleSettings = settings.scheduleSettings - if (!wallpaperSettings.enableChanger) return if (wallpaperSettings.homeAlbumName.isNullOrEmpty() || wallpaperSettings.lockAlbumName.isNullOrEmpty()) { settingsViewModel.onEvent(SettingsEvent.SetChangerToggle(false)) return } - val shouldScheduleAlarm = if (scheduleSettings.scheduleSeparately) { !(isPendingIntentSet(Type.HOME.ordinal) && isPendingIntentSet(Type.LOCK.ordinal)) } else { !isPendingIntentSet(Type.SINGLE.ordinal) } - if (shouldScheduleAlarm) { scheduleWallpaperAlarm(settings, scheduler) } @@ -158,7 +127,6 @@ class MainActivity : ComponentActivity() { ) { val scheduleSettings = settings.scheduleSettings val wallpaperSettings = settings.wallpaperSettings - scheduler.scheduleWallpaperAlarm( WallpaperAlarmItem( homeInterval = scheduleSettings.homeInterval, diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/PaperizeApp.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/PaperizeApp.kt index 95a47b36..1923403e 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/PaperizeApp.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/PaperizeApp.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -28,6 +27,8 @@ import com.anthonyla.paperize.feature.wallpaper.presentation.add_album_screen.Ad import com.anthonyla.paperize.feature.wallpaper.presentation.album.AlbumsEvent import com.anthonyla.paperize.feature.wallpaper.presentation.album.AlbumsViewModel import com.anthonyla.paperize.feature.wallpaper.presentation.album_view_screen.AlbumViewScreen +import com.anthonyla.paperize.feature.wallpaper.presentation.album_view_screen.AlbumScreenViewModel +import com.anthonyla.paperize.feature.wallpaper.presentation.album_view_screen.AlbumViewEvent import com.anthonyla.paperize.feature.wallpaper.presentation.folder_view_screen.FolderEvent import com.anthonyla.paperize.feature.wallpaper.presentation.folder_view_screen.FolderViewModel import com.anthonyla.paperize.feature.wallpaper.presentation.folder_view_screen.FolderViewScreen @@ -42,8 +43,6 @@ import com.anthonyla.paperize.feature.wallpaper.presentation.sort_view_screen.So import com.anthonyla.paperize.feature.wallpaper.presentation.sort_view_screen.SortViewModel import com.anthonyla.paperize.feature.wallpaper.presentation.sort_view_screen.SortViewScreen import com.anthonyla.paperize.feature.wallpaper.presentation.startup_screen.StartupScreen -import com.anthonyla.paperize.feature.wallpaper.presentation.wallpaper_screen.WallpaperEvent -import com.anthonyla.paperize.feature.wallpaper.presentation.wallpaper_screen.WallpaperScreenViewModel import com.anthonyla.paperize.feature.wallpaper.presentation.wallpaper_view_screen.WallpaperViewScreen import com.anthonyla.paperize.feature.wallpaper.util.navigation.AddAlbum import com.anthonyla.paperize.feature.wallpaper.util.navigation.AlbumView @@ -59,11 +58,9 @@ import com.anthonyla.paperize.feature.wallpaper.util.navigation.WallpaperView import com.anthonyla.paperize.feature.wallpaper.util.navigation.animatedScreen import com.anthonyla.paperize.feature.wallpaper.wallpaper_alarmmanager.WallpaperAlarmItem import com.anthonyla.paperize.feature.wallpaper.wallpaper_alarmmanager.WallpaperAlarmSchedulerImpl -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -72,48 +69,20 @@ fun PaperizeApp( scheduler : WallpaperAlarmSchedulerImpl, albumsViewModel: AlbumsViewModel = hiltViewModel(), settingsViewModel: SettingsViewModel = hiltViewModel(), - wallpaperScreenViewModel: WallpaperScreenViewModel = hiltViewModel(), + albumScreenViewModel: AlbumScreenViewModel = hiltViewModel(), addAlbumViewModel: AddAlbumViewModel = hiltViewModel(), folderViewModel: FolderViewModel = hiltViewModel(), sortViewModel: SortViewModel = hiltViewModel() ) { val context = LocalContext.current val navController = rememberNavController() - val albumState = albumsViewModel.state.collectAsStateWithLifecycle() - val selectedState = wallpaperScreenViewModel.state.collectAsStateWithLifecycle() + val scope = rememberCoroutineScope() + var job by remember { mutableStateOf(null) } val settingsState = settingsViewModel.state.collectAsStateWithLifecycle() + val albumState = albumsViewModel.state.collectAsStateWithLifecycle() val sortState = sortViewModel.state.collectAsStateWithLifecycle() val folderState = folderViewModel.state.collectAsStateWithLifecycle() - var job by remember { mutableStateOf(null) } - val scope = rememberCoroutineScope() - - // Process albums that need initialization or deletion - LaunchedEffect(albumState.value) { - withContext(Dispatchers.Default) { - albumState.value.albumsWithWallpapers.asSequence() - .filter { album -> - (!album.album.initialized && (album.wallpapers.isNotEmpty() || album.folders.isNotEmpty())) || - (album.wallpapers.isEmpty() && album.folders.isEmpty() && album.album.initialized) - } - .forEach { album -> - when { - !album.album.initialized -> { - albumsViewModel.onEvent(AlbumsEvent.InitializeAlbum(album)) - } - else -> { - if (navController.currentDestination?.route == Home::class.simpleName) { - try { - navController.popBackStack(inclusive = false) - } catch (_: Exception) { - navController.navigate(Home) - } - } - albumsViewModel.onEvent(AlbumsEvent.DeleteAlbumWithWallpapers(album)) - } - } - } - } - } + albumsViewModel.onEvent(AlbumsEvent.Refresh) NavHost( navController = navController, @@ -143,8 +112,8 @@ fun PaperizeApp( animatedScreen(animate = settingsState.value.themeSettings.animate) { HomeScreen( albums = albumState.value.albumsWithWallpapers, - homeSelectedAlbum = selectedState.value.selectedAlbum?.find { it.album.initialAlbumName == settingsState.value.wallpaperSettings.homeAlbumName }, - lockSelectedAlbum = selectedState.value.selectedAlbum?.find { it.album.initialAlbumName == settingsState.value.wallpaperSettings.lockAlbumName }, + homeSelectedAlbum = albumState.value.selectedAlbum.find { it.album.initialAlbumName == settingsState.value.wallpaperSettings.homeAlbumName }, + lockSelectedAlbum = albumState.value.selectedAlbum.find { it.album.initialAlbumName == settingsState.value.wallpaperSettings.lockAlbumName }, themeSettings = settingsState.value.themeSettings, wallpaperSettings = settingsState.value.wallpaperSettings, scheduleSettings = settingsState.value.scheduleSettings, @@ -194,25 +163,25 @@ fun PaperizeApp( }, onStop = { lock, home -> - if (!selectedState.value.selectedAlbum.isNullOrEmpty()) { + if (albumState.value.selectedAlbum.isNotEmpty()) { val notSameAlbum = settingsState.value.wallpaperSettings.homeAlbumName != settingsState.value.wallpaperSettings.lockAlbumName when { lock && home -> { settingsState.value.wallpaperSettings.lockAlbumName?.let { - wallpaperScreenViewModel.onEvent(WallpaperEvent.RemoveSelectedAlbum(it)) + albumsViewModel.onEvent(AlbumsEvent.RemoveSelectedAlbum(it)) } } lock -> { if (notSameAlbum) { settingsState.value.wallpaperSettings.lockAlbumName?.let { - wallpaperScreenViewModel.onEvent(WallpaperEvent.RemoveSelectedAlbum(it)) + albumsViewModel.onEvent(AlbumsEvent.RemoveSelectedAlbum(it)) } } } home -> { if (notSameAlbum) { settingsState.value.wallpaperSettings.homeAlbumName?.let { - wallpaperScreenViewModel.onEvent(WallpaperEvent.RemoveSelectedAlbum(it)) + albumsViewModel.onEvent(AlbumsEvent.RemoveSelectedAlbum(it)) } } } @@ -222,7 +191,7 @@ fun PaperizeApp( } }, onToggleChanger = { enableWallpaperChanger -> - if (!selectedState.value.selectedAlbum.isNullOrEmpty() && !settingsState.value.wallpaperSettings.homeAlbumName.isNullOrEmpty() && !settingsState.value.wallpaperSettings.lockAlbumName.isNullOrEmpty()) { + if (albumState.value.selectedAlbum.isNotEmpty() && !settingsState.value.wallpaperSettings.homeAlbumName.isNullOrEmpty() && !settingsState.value.wallpaperSettings.lockAlbumName.isNullOrEmpty()) { settingsViewModel.onEvent(SettingsEvent.SetChangerToggle(enableWallpaperChanger)) if (enableWallpaperChanger) { job?.cancel() @@ -258,7 +227,7 @@ fun PaperizeApp( homeAlbumName = album.album.initialAlbumName, lockAlbumName = album.album.initialAlbumName, )) - wallpaperScreenViewModel.onEvent(WallpaperEvent.AddSelectedAlbum( + albumsViewModel.onEvent(AlbumsEvent.AddSelectedAlbum( album = album, deselectAlbumName = if (notSameAlbum) settingsState.value.wallpaperSettings.lockAlbumName else null) ) @@ -268,7 +237,7 @@ fun PaperizeApp( homeAlbumName = null, lockAlbumName = album.album.initialAlbumName, )) - wallpaperScreenViewModel.onEvent(WallpaperEvent.AddSelectedAlbum( + albumsViewModel.onEvent(AlbumsEvent.AddSelectedAlbum( album = album, deselectAlbumName = if (notSameAlbum) settingsState.value.wallpaperSettings.lockAlbumName else null) ) @@ -278,7 +247,7 @@ fun PaperizeApp( homeAlbumName = album.album.initialAlbumName, lockAlbumName = null, )) - wallpaperScreenViewModel.onEvent(WallpaperEvent.AddSelectedAlbum( + albumsViewModel.onEvent(AlbumsEvent.AddSelectedAlbum( album = album, deselectAlbumName = if (notSameAlbum) settingsState.value.wallpaperSettings.homeAlbumName else null) ) @@ -340,19 +309,19 @@ fun PaperizeApp( }, onHomeCheckedChange = { setHome -> settingsViewModel.onEvent(SettingsEvent.SetHome(setHome)) - if (!selectedState.value.selectedAlbum.isNullOrEmpty() && !setHome && !settingsState.value.wallpaperSettings.setLockWallpaper) { + if (albumState.value.selectedAlbum.isNotEmpty() && !setHome && !settingsState.value.wallpaperSettings.setLockWallpaper) { settingsViewModel.onEvent(SettingsEvent.SetChangerToggle(false)) - selectedState.value.selectedAlbum?.let { wallpaperScreenViewModel.onEvent(WallpaperEvent.Reset) } + albumState.value.selectedAlbum.let { albumsViewModel.onEvent(AlbumsEvent.Reset) } scheduler.cancelWallpaperAlarm() } - else if (!selectedState.value.selectedAlbum.isNullOrEmpty() && (setHome && !settingsState.value.wallpaperSettings.setLockWallpaper) || (!setHome && settingsState.value.wallpaperSettings.setLockWallpaper)) { + else if (albumState.value.selectedAlbum.isNotEmpty() && (setHome && !settingsState.value.wallpaperSettings.setLockWallpaper) || (!setHome && settingsState.value.wallpaperSettings.setLockWallpaper)) { settingsViewModel.onEvent(SettingsEvent.SetScheduleSeparately(false)) job?.cancel() job = scope.launch { /*if (!setHome && settingsState.value.wallpaperSettings.setLockWallpaper) { - val homeAlbum = selectedState.value.selectedAlbum?.find { it.album.initialAlbumName == settingsState.value.wallpaperSettings.homeAlbumName } + val homeAlbum = albumState.value.selectedAlbum?.find { it.album.initialAlbumName == settingsState.value.wallpaperSettings.homeAlbumName } if (homeAlbum != null) { - wallpaperScreenViewModel.onEvent(WallpaperEvent.UpdateSelectedAlbum( + albumsViewModel.onEvent(AlbumsEvent.UpdateSelectedAlbum( album = homeAlbum.copy( album = homeAlbum.album.copy( lockWallpapersInQueue = homeAlbum.album.homeWallpapersInQueue, @@ -383,7 +352,7 @@ fun PaperizeApp( scheduler.scheduleRefresh() } } - else if (!selectedState.value.selectedAlbum.isNullOrEmpty() && settingsState.value.wallpaperSettings.enableChanger) { + else if (albumState.value.selectedAlbum.isNotEmpty() && settingsState.value.wallpaperSettings.enableChanger) { job?.cancel() job = scope.launch { scheduler.updateWallpaper(settingsState.value.scheduleSettings.scheduleSeparately, setHome, settingsState.value.wallpaperSettings.setLockWallpaper) @@ -392,14 +361,14 @@ fun PaperizeApp( }, onLockCheckedChange = { setLock -> settingsViewModel.onEvent(SettingsEvent.SetLock(setLock)) - if (selectedState.value.selectedAlbum!= null && !setLock && !settingsState.value.wallpaperSettings.setHomeWallpaper) { + if (!setLock && !settingsState.value.wallpaperSettings.setHomeWallpaper) { settingsViewModel.onEvent(SettingsEvent.SetChangerToggle(false)) - selectedState.value.selectedAlbum?.let { - wallpaperScreenViewModel.onEvent(WallpaperEvent.Reset) + albumState.value.selectedAlbum.let { + albumsViewModel.onEvent(AlbumsEvent.Reset) } scheduler.cancelWallpaperAlarm() } - else if (selectedState.value.selectedAlbum!= null && (setLock && !settingsState.value.wallpaperSettings.setHomeWallpaper) || (!setLock && settingsState.value.wallpaperSettings.setHomeWallpaper)) { + else if ((setLock && !settingsState.value.wallpaperSettings.setHomeWallpaper) || (!setLock && settingsState.value.wallpaperSettings.setHomeWallpaper)) { settingsViewModel.onEvent(SettingsEvent.SetScheduleSeparately(false)) job?.cancel() job = scope.launch { @@ -424,7 +393,7 @@ fun PaperizeApp( scheduler.scheduleRefresh() } } - else if (!selectedState.value.selectedAlbum.isNullOrEmpty() && settingsState.value.wallpaperSettings.enableChanger) { + else if (albumState.value.selectedAlbum.isNotEmpty() && settingsState.value.wallpaperSettings.enableChanger) { job?.cancel() job = scope.launch { delay(1000) @@ -434,7 +403,7 @@ fun PaperizeApp( }, onScheduleSeparatelyChange = { changeSeparately -> settingsViewModel.onEvent(SettingsEvent.SetScheduleSeparately(changeSeparately)) - if (!selectedState.value.selectedAlbum.isNullOrEmpty() && settingsState.value.wallpaperSettings.enableChanger) { + if (albumState.value.selectedAlbum.isNotEmpty() && settingsState.value.wallpaperSettings.enableChanger) { job?.cancel() job = scope.launch { delay(1000) @@ -564,11 +533,9 @@ fun PaperizeApp( initialAlbumName = addAlbum.initialAlbumName, onBackClick = { navController.navigateUp() - addAlbumViewModel.onEvent(AddAlbumEvent.Reset) }, onConfirmation = { navController.navigateUp() - addAlbumViewModel.onEvent(AddAlbumEvent.Reset) }, onShowWallpaperView = { navController.navigate(WallpaperView(it)) @@ -633,7 +600,12 @@ fun PaperizeApp( )) navController.navigateUp() } - else if (navController.currentBackStackEntry?.destination?.route == AlbumView::class.simpleName) { + else if (previousScreen?.contains(AlbumView::class.simpleName.toString()) ?: false) { + albumScreenViewModel.onEvent(AlbumViewEvent.LoadFoldersAndWallpapers( + folders = sortState.value.folders, + wallpapers = sortState.value.wallpapers + )) + navController.navigateUp() } }, onBackClick = { navController.navigateUp() }, @@ -644,10 +616,10 @@ fun PaperizeApp( // Navigate to the album view screen to view folders and wallpapers in an album animatedScreen(animate = settingsState.value.themeSettings.animate) { backStackEntry -> val albumView: AlbumView = backStackEntry.toRoute() - val albumWithWallpaper = albumState.value.albumsWithWallpapers.find { it.album.initialAlbumName == albumView.initialAlbumName } - if (albumWithWallpaper != null) { + if (albumView.initialAlbumName.isNotEmpty()) { + albumScreenViewModel.onEvent(AlbumViewEvent.LoadAlbum(albumView.initialAlbumName)) AlbumViewScreen( - album = albumWithWallpaper, + albumScreenViewModel = albumScreenViewModel , animate = settingsState.value.themeSettings.animate, onBackClick = { navController.navigateUp() }, onShowWallpaperView = { @@ -661,16 +633,16 @@ fun PaperizeApp( }, onDeleteAlbum = { navController.navigateUp() - albumsViewModel.onEvent(AlbumsEvent.DeleteAlbumWithWallpapers(albumWithWallpaper)) }, - onAlbumNameChange = { name, originalAlbumWithWallpaper -> - albumsViewModel.onEvent(AlbumsEvent.ChangeAlbumName(name, originalAlbumWithWallpaper)) + onSortClick = { folders, wallpapers -> + if (folders.isNotEmpty() || wallpapers.isNotEmpty()) { + sortViewModel.onEvent(SortEvent.LoadSortView(folders, wallpapers)) + navController.navigate(SortView) + } }, - onSelectionDeleted = { - albumsViewModel.onEvent(AlbumsEvent.RefreshAlbums) - } ) - } else { navController.navigateUp() } + } + else { navController.navigateUp() } } // Navigate to the settings screen to change app settings @@ -701,7 +673,7 @@ fun PaperizeApp( }, onResetClick = { settingsViewModel.onEvent(SettingsEvent.Reset) - wallpaperScreenViewModel.onEvent(WallpaperEvent.Reset) + albumsViewModel.onEvent(AlbumsEvent.Reset) albumsViewModel.onEvent(AlbumsEvent.Reset) addAlbumViewModel.onEvent(AddAlbumEvent.Reset) scheduler.cancelWallpaperAlarm() diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/add_album_screen/AddAlbumScreen.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/add_album_screen/AddAlbumScreen.kt index 63a0045a..f3cca638 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/add_album_screen/AddAlbumScreen.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/add_album_screen/AddAlbumScreen.kt @@ -28,7 +28,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.anthonyla.paperize.feature.wallpaper.domain.model.Folder import com.anthonyla.paperize.feature.wallpaper.domain.model.Wallpaper @@ -80,11 +79,8 @@ fun AddAlbumScreen( val persistedUriPermissions = context.contentResolver.persistedUriPermissions if (persistedUriPermissions.any { it.uri == uri }) uri.toString() else null } - if (uriList.isNotEmpty()) { - withContext(Dispatchers.Main) { - addAlbumViewModel.onEvent(AddAlbumEvent.AddWallpapers(uriList)) - } + addAlbumViewModel.onEvent(AddAlbumEvent.AddWallpapers(uriList)) } addAlbumViewModel.onEvent(AddAlbumEvent.SetLoading(false)) } @@ -168,9 +164,7 @@ fun AddAlbumScreen( }, bottomBar = { if (addAlbumState.value.isLoading) { - LinearProgressIndicator( - modifier = Modifier.fillMaxWidth() - ) + LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) } }, content = { diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/add_album_screen/AddAlbumViewModel.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/add_album_screen/AddAlbumViewModel.kt index a74e4e1b..7ae0c68e 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/add_album_screen/AddAlbumViewModel.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/add_album_screen/AddAlbumViewModel.kt @@ -1,11 +1,10 @@ package com.anthonyla.paperize.feature.wallpaper.presentation.add_album_screen - -import android.R.attr.order import android.app.Application import android.content.Context import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope +import com.anthonyla.paperize.core.compress import com.anthonyla.paperize.core.getFolderLastModified import com.anthonyla.paperize.core.getFolderNameFromUri import com.anthonyla.paperize.core.getImageMetadata @@ -51,6 +50,7 @@ class AddAlbumViewModel @Inject constructor( folder.copy( initialAlbumName = event.initialAlbumName, key = event.initialAlbumName.hashCode() + folder.hashCode(), + coverUri = folder.wallpapers.firstOrNull()?.wallpaperUri ?: folder.coverUri, wallpapers = folder.wallpapers.map { it.copy( initialAlbumName = event.initialAlbumName, @@ -59,20 +59,18 @@ class AddAlbumViewModel @Inject constructor( } ) } - val totalWallpapers: List = folders.flatMap { it.wallpapers } + wallpapers val albumWithWallpaperAndFolder = AlbumWithWallpaperAndFolder( album = Album( initialAlbumName = event.initialAlbumName, displayedAlbumName = event.initialAlbumName, - coverUri = wallpapers.firstOrNull()?.wallpaperUri ?: folders.firstOrNull()?.coverUri, + coverUri = folders.firstOrNull()?.coverUri ?: wallpapers.firstOrNull()?.wallpaperUri ?: "", homeWallpapersInQueue = emptyList(), lockWallpapersInQueue = emptyList(), - initialized = false, selected = false ), wallpapers = wallpapers, folders = folders, - totalWallpapers = totalWallpapers + totalWallpapers = emptyList() ) repository.upsertAlbumWithWallpaperAndFolder(albumWithWallpaperAndFolder) _state.update { AddAlbumState() } @@ -106,22 +104,23 @@ class AddAlbumViewModel @Inject constructor( val newWallpapers = event.wallpaperUris.filter { wallpaperUri -> wallpaperUri !in existingWallpapers } + val wallpapers = _state.value.wallpapers.plus( + newWallpapers.mapIndexed { index, wallpaperUri -> + val metadata = getImageMetadata(context, wallpaperUri) + Wallpaper( + initialAlbumName = "", + wallpaperUri = wallpaperUri.compress("content://com.android.externalstorage.documents/"), + fileName = metadata.filename, + dateModified = metadata.lastModified, + order = index + _state.value.wallpapers.size, + key = 0 + ) + } + ) _state.update { it.copy( - wallpapers = it.wallpapers.plus( - newWallpapers.mapIndexed { index, wallpaperUri -> - val metadata = getImageMetadata(context, wallpaperUri) - Wallpaper( - initialAlbumName = "", - wallpaperUri = wallpaperUri, - fileName = metadata.filename, - dateModified = metadata.lastModified, - order = index + it.wallpapers.size, - key = 0 - ) - } - ), - isEmpty = event.wallpaperUris.isEmpty(), + wallpapers = wallpapers, + isEmpty = false, selectionState = SelectionState(), isLoading = false ) @@ -131,7 +130,9 @@ class AddAlbumViewModel @Inject constructor( is AddAlbumEvent.AddFolder -> { viewModelScope.launch { - if (event.directoryUri in _state.value.folders.map { it.folderUri }) { return@launch } + if (event.directoryUri in _state.value.folders.map { it.folderUri }) { + return@launch + } _state.update { it.copy(isLoading = true) } val wallpapers = getWallpaperFromFolder(event.directoryUri, context) val folderName = getFolderNameFromUri(event.directoryUri, context) @@ -154,8 +155,8 @@ class AddAlbumViewModel @Inject constructor( isLoading = false ) } + } } - } is AddAlbumEvent.SelectAll -> { viewModelScope.launch { @@ -222,7 +223,7 @@ class AddAlbumViewModel @Inject constructor( it.copy( selectionState = it.selectionState.copy( selectedFolders = newSelectedFolders, - selectedCount = newSelectedFolders.size + it.selectionState.selectedWallpapers.size, + selectedCount = it.selectionState.selectedCount - 1, allSelected = false ) ) @@ -238,7 +239,7 @@ class AddAlbumViewModel @Inject constructor( it.copy( selectionState = it.selectionState.copy( selectedWallpapers = newSelectedWallpapers, - selectedCount = newSelectedWallpapers.size + it.selectionState.selectedFolders.size, + selectedCount = it.selectionState.selectedCount - 1, allSelected = false ) ) diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/add_album_screen/components/AddAlbumSmallTopBar.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/add_album_screen/components/AddAlbumSmallTopBar.kt index 00a99f70..37626efd 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/add_album_screen/components/AddAlbumSmallTopBar.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/add_album_screen/components/AddAlbumSmallTopBar.kt @@ -1,5 +1,6 @@ package com.anthonyla.paperize.feature.wallpaper.presentation.add_album_screen.components +import android.R.attr.contentDescription import androidx.compose.animation.core.LinearOutSlowInEasing import androidx.compose.animation.core.RepeatMode import androidx.compose.animation.core.animateFloat @@ -21,6 +22,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.Sort import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.RadioButtonUnchecked import androidx.compose.material.icons.outlined.Add import androidx.compose.material.icons.outlined.Delete @@ -163,7 +165,7 @@ fun AddAlbumSmallTopBar( } ) } - Text("$selectedCount selected") + Text(stringResource(R.string.selected, selectedCount)) } } }, @@ -212,7 +214,7 @@ fun AddAlbumSmallTopBar( } else { IconButton(onClick = { showSelectionDeleteDialog = true }) { Icon( - imageVector = Icons.Outlined.Delete, + imageVector = if (showSelectionDeleteDialog) Icons.Filled.Delete else Icons.Outlined.Delete, contentDescription = stringResource(R.string.select_all_images_for_deletion), modifier = Modifier.padding(6.dp) ) diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/AlbumsEvent.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/AlbumsEvent.kt index 3ebb6bd9..55457c78 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/AlbumsEvent.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/AlbumsEvent.kt @@ -1,20 +1,12 @@ package com.anthonyla.paperize.feature.wallpaper.presentation.album import com.anthonyla.paperize.feature.wallpaper.domain.model.AlbumWithWallpaperAndFolder + sealed class AlbumsEvent { - data object RefreshAlbums: AlbumsEvent() + data object DeselectSelected: AlbumsEvent() data object Reset: AlbumsEvent() + data object Refresh: AlbumsEvent() - data class DeleteAlbumWithWallpapers( - val albumWithWallpaperAndFolder: AlbumWithWallpaperAndFolder - ): AlbumsEvent() - - data class ChangeAlbumName( - val title: String, - val albumWithWallpaperAndFolder: AlbumWithWallpaperAndFolder - ): AlbumsEvent() - - data class InitializeAlbum( - val albumWithWallpaperAndFolder: AlbumWithWallpaperAndFolder - ): AlbumsEvent() + data class AddSelectedAlbum(val album: AlbumWithWallpaperAndFolder, val deselectAlbumName: String? = null): AlbumsEvent() + data class RemoveSelectedAlbum(val deselectAlbumName: String): AlbumsEvent() } \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/AlbumsState.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/AlbumsState.kt index dab2175e..8b7d1aa9 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/AlbumsState.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/AlbumsState.kt @@ -2,6 +2,7 @@ package com.anthonyla.paperize.feature.wallpaper.presentation.album import com.anthonyla.paperize.feature.wallpaper.domain.model.AlbumWithWallpaperAndFolder -data class AlbumsState ( - val albumsWithWallpapers: List = emptyList() +data class AlbumsState( + val albumsWithWallpapers: List = emptyList(), + val selectedAlbum: List = emptyList() ) \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/AlbumsViewModel.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/AlbumsViewModel.kt index f6afbd9d..51bbf05d 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/AlbumsViewModel.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/AlbumsViewModel.kt @@ -2,16 +2,18 @@ package com.anthonyla.paperize.feature.wallpaper.presentation.album import android.app.Application import android.content.Context -import androidx.core.net.toUri -import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.anthonyla.paperize.core.findFirstValidUri import com.anthonyla.paperize.core.getWallpaperFromFolder +import com.anthonyla.paperize.core.isDirectory +import com.anthonyla.paperize.core.isValidUri import com.anthonyla.paperize.feature.wallpaper.domain.model.AlbumWithWallpaperAndFolder import com.anthonyla.paperize.feature.wallpaper.domain.repository.AlbumRepository -import com.lazygeniouz.dfc.file.DocumentFileCompat import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine @@ -27,14 +29,16 @@ class AlbumsViewModel @Inject constructor ( ) : AndroidViewModel(application) { private val context: Context get() = getApplication().applicationContext - + private val _state = MutableStateFlow(AlbumsState()) val state = combine( loadAlbumsFlow(), + loadSelectedAlbumFlow(), _state - ) { albums: List, currentState: AlbumsState -> + ) { albums: List, selectedAlbum: List, currentState: AlbumsState -> currentState.copy( - albumsWithWallpapers = albums + albumsWithWallpapers = albums, + selectedAlbum = selectedAlbum ) }.stateIn( scope = viewModelScope, @@ -43,39 +47,41 @@ class AlbumsViewModel @Inject constructor ( ) private fun loadAlbumsFlow() = repository.getAlbumsWithWallpaperAndFolder() + private fun loadSelectedAlbumFlow() = repository.getSelectedAlbums() init { - updateAlbums() + viewModelScope.launch { + state.collect { newState -> _state.value = newState } + } } fun onEvent(event: AlbumsEvent) { when (event) { - is AlbumsEvent.DeleteAlbumWithWallpapers -> { + is AlbumsEvent.AddSelectedAlbum -> { viewModelScope.launch { - repository.cascadeDeleteAlbum(event.albumWithWallpaperAndFolder.album) + event.deselectAlbumName?.let { + repository.updateAlbumSelection(it, false) + } + repository.updateAlbumSelection(event.album.album.initialAlbumName, true) } } - - is AlbumsEvent.ChangeAlbumName -> { + + is AlbumsEvent.RemoveSelectedAlbum -> { viewModelScope.launch { - if (!(_state.value.albumsWithWallpapers.any { it.album.displayedAlbumName == event.title })) { - repository.updateAlbum( - event.albumWithWallpaperAndFolder.album.copy(displayedAlbumName = event.title) - ) - } + repository.updateAlbumSelection(event.deselectAlbumName, false) } } - is AlbumsEvent.InitializeAlbum -> { + is AlbumsEvent.DeselectSelected -> { viewModelScope.launch { - repository.updateAlbum( - event.albumWithWallpaperAndFolder.album.copy(initialized = true) - ) + repository.deselectAllAlbums() } } - is AlbumsEvent.RefreshAlbums -> { - updateAlbums() + is AlbumsEvent.Refresh -> { + viewModelScope.launch { + updateAlbums() + } } is AlbumsEvent.Reset -> { @@ -88,109 +94,67 @@ class AlbumsViewModel @Inject constructor ( private fun updateAlbums() { viewModelScope.launch { - var albumWithWallpapers = repository.getAlbumsWithWallpaperAndFolder().first() - albumWithWallpapers.forEach { albumWithWallpaper -> - // Delete invalid wallpapers - val invalidWallpapers = albumWithWallpaper.wallpapers.filterNot { wallpaper -> - try { - val file = DocumentFileCompat.fromSingleUri(context, wallpaper.wallpaperUri.toUri()) - file?.exists() == true - } catch (e: Exception) { - val file = DocumentFile.fromSingleUri(context, wallpaper.wallpaperUri.toUri()) - file?.exists() == true - } - } - if (invalidWallpapers.isNotEmpty()) { - repository.deleteWallpaperList(invalidWallpapers) + _state.value.albumsWithWallpapers.forEach { album -> + // Remove invalid wallpapers + val album = album + val validWallpapers = async { + album.wallpapers + .asSequence() + .filter { isValidUri(context, it.wallpaperUri) } + .sortedBy { it.order } + .mapIndexed { index, wallpaper -> wallpaper.copy(order = index) } + .toList() } - // Update folder cover uri and wallpapers uri - albumWithWallpaper.folders.forEach { folder -> - try { - DocumentFileCompat.fromTreeUri(context, folder.folderUri.toUri()) - ?.let { folderDirectory -> - if (!folderDirectory.isDirectory()) { - repository.deleteFolder(folder) - } else { - val wallpapers = getWallpaperFromFolder(folder.folderUri, context) - val folderCoverFile = folder.coverUri?.let { - DocumentFileCompat.fromSingleUri(context, it.toUri()) - } - val folderCover = - folderCoverFile?.takeIf { it.exists() }?.uri?.toString() - ?: (wallpapers.randomOrNull()?.wallpaperUri ?: "") - repository.updateFolder( - folder.copy( - coverUri = folderCover, - wallpapers = wallpapers - ) - ) - } - } - } catch (e: Exception) { - DocumentFile.fromTreeUri(context, folder.folderUri.toUri()) - ?.let { folderDirectory -> - if (!folderDirectory.isDirectory) { - repository.deleteFolder(folder) - } else { - val wallpapers = getWallpaperFromFolder(folder.folderUri, context) - try { - val folderCoverFile = folder.coverUri?.let { - DocumentFileCompat.fromSingleUri(context, it.toUri()) - } - val folderCover = - folderCoverFile?.takeIf { it.exists() }?.uri?.toString() - ?: (wallpapers.randomOrNull()?.wallpaperUri ?: "") - repository.updateFolder( - folder.copy( - coverUri = folderCover, - wallpapers = wallpapers + // Remove invalid folders and inner wallpapers + val validFolders = async { + album.folders + .asSequence() + .filterNot { isDirectory(context, it.folderUri) } + .map { folder -> + async { + val existingWallpapers = folder.wallpapers + .asSequence() + .filter { isValidUri(context, it.wallpaperUri) } + .mapIndexed { index, wallpaper -> wallpaper.copy(order = index) } + .toList() + val newWallpapers = + getWallpaperFromFolder(folder.folderUri, context) + .asSequence() + .filterNot { new -> existingWallpapers.any { it.wallpaperUri == new.wallpaperUri } } + .mapIndexed { index, wallpaper -> + wallpaper.copy( + initialAlbumName = album.album.initialAlbumName, + order = existingWallpapers.size + 1 + index, + key = album.album.initialAlbumName.hashCode() + + folder.folderUri.hashCode() + + wallpaper.wallpaperUri.hashCode() ) - ) - } catch (_: Exception) { - val folderCoverFile = folder.coverUri?.let { - DocumentFile.fromSingleUri(context, it.toUri()) } - val folderCover = - folderCoverFile?.takeIf { it.exists() }?.uri?.toString() - ?: (wallpapers.randomOrNull()?.wallpaperUri ?: "") - repository.updateFolder( - folder.copy( - coverUri = folderCover, - wallpapers = wallpapers - ) - ) - } - } - } - } - } - // Delete empty albums - albumWithWallpapers = repository.getAlbumsWithWallpaperAndFolder().first() - albumWithWallpapers.forEach { album -> - if (album.wallpapers.isEmpty() && album.folders.all { it.wallpapers.isEmpty() }) { - repository.deleteAlbum(album.album) - } else { - // Update album cover uri if null or invalid - try { - val albumCoverFile = album.album.coverUri?.toUri() - ?.let { DocumentFileCompat.fromSingleUri(context, it) } - if (albumCoverFile == null || !albumCoverFile.exists()) { - val newCoverUri = - findFirstValidUri(context, album.wallpapers, album.folders) - repository.updateAlbum(album.album.copy(coverUri = newCoverUri)) - } - } catch (e: Exception) { - val albumCoverFile = album.album.coverUri?.toUri() - ?.let { DocumentFile.fromSingleUri(context, it) } - if (albumCoverFile == null || !albumCoverFile.exists()) { - val newCoverUri = - findFirstValidUri(context, album.wallpapers, album.folders) - repository.updateAlbum(album.album.copy(coverUri = newCoverUri)) + .toList() + val combinedWallpapers = existingWallpapers + newWallpapers + folder.copy( + coverUri = combinedWallpapers.firstOrNull()?.wallpaperUri ?: "", + wallpapers = combinedWallpapers + ) } } - } + .toList() + .awaitAll() } + + val folders = validFolders.await() + val wallpapers = validWallpapers.await() + val coverUri = findFirstValidUri(context, folders, wallpapers) + val totalWallpapers = folders.flatMap { it.wallpapers } + wallpapers + repository.upsertAlbumWithWallpaperAndFolder( + album.copy( + album = album.album.copy(coverUri = coverUri), + wallpapers = wallpapers, + folders = folders, + totalWallpapers = totalWallpapers + ) + ) } } } diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/components/AlbumItem.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/components/AlbumItem.kt index 192ae570..5cf79f3e 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/components/AlbumItem.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/components/AlbumItem.kt @@ -8,11 +8,8 @@ import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Block import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -24,11 +21,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.anthonyla.paperize.R +import androidx.core.net.toUri +import com.anthonyla.paperize.core.decompress import com.anthonyla.paperize.core.isValidUri import com.anthonyla.paperize.feature.wallpaper.domain.model.Album import com.skydoves.landscapist.ImageOptions @@ -59,7 +56,9 @@ fun AlbumItem( Box { if (showCoverUri) { GlideImage( - imageModel = { album.coverUri }, + imageModel = { + album.coverUri?.decompress("content://com.android.externalstorage.documents/")?.toUri() + }, imageOptions = ImageOptions( contentScale = ContentScale.Crop, alignment = Alignment.Center, diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/components/FolderItem.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/components/FolderItem.kt index 4c1109f4..10158b55 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/components/FolderItem.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/components/FolderItem.kt @@ -44,6 +44,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.net.toUri import com.anthonyla.paperize.R +import com.anthonyla.paperize.core.decompress import com.anthonyla.paperize.core.isValidUri import com.anthonyla.paperize.feature.wallpaper.domain.model.Folder import com.skydoves.landscapist.ImageOptions @@ -105,7 +106,7 @@ fun FolderItem( Box (modifier = Modifier.fillMaxHeight(0.8f)) { if (showCoverUri) { GlideImage( - imageModel = { folder.coverUri?.toUri() }, + imageModel = { folder.coverUri?.decompress("content://com.android.externalstorage.documents/")?.toUri() }, imageOptions = ImageOptions( contentScale = ContentScale.Crop, alignment = Alignment.Center, diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/components/WallpaperItem.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/components/WallpaperItem.kt index 39ed6783..9c93c390 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/components/WallpaperItem.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album/components/WallpaperItem.kt @@ -34,6 +34,8 @@ import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import androidx.core.net.toUri import com.anthonyla.paperize.R +import com.anthonyla.paperize.core.decompress +import com.anthonyla.paperize.core.isValidUri import com.skydoves.landscapist.ImageOptions import com.skydoves.landscapist.glide.GlideImage @@ -62,16 +64,6 @@ fun WallpaperItem( if (selected) 24.dp else 16.dp } - fun isValidUri(context: Context, uriString: String?): Boolean { - val uri = uriString?.toUri() - return try { - uri?.let { - val inputStream = context.contentResolver.openInputStream(it) - inputStream?.close() - } - true - } catch (e: Exception) { false } - } val showUri by remember { mutableStateOf(isValidUri(context, wallpaperUri)) } Box( @@ -96,7 +88,7 @@ fun WallpaperItem( ) { if (showUri) { GlideImage( - imageModel = { wallpaperUri }, + imageModel = { wallpaperUri.decompress("content://com.android.externalstorage.documents/").toUri() }, imageOptions = ImageOptions( requestSize = IntSize(200, 200), alignment = Alignment.Center, diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumScreenViewModel.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumScreenViewModel.kt new file mode 100644 index 00000000..a0711ba5 --- /dev/null +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumScreenViewModel.kt @@ -0,0 +1,315 @@ +package com.anthonyla.paperize.feature.wallpaper.presentation.album_view_screen + + +import android.app.Application +import android.content.Context +import android.util.Log +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.viewModelScope +import com.anthonyla.paperize.core.compress +import com.anthonyla.paperize.core.getFolderLastModified +import com.anthonyla.paperize.core.getFolderNameFromUri +import com.anthonyla.paperize.core.getImageMetadata +import com.anthonyla.paperize.core.getWallpaperFromFolder +import com.anthonyla.paperize.feature.wallpaper.domain.model.AlbumWithWallpaperAndFolder +import com.anthonyla.paperize.feature.wallpaper.domain.model.Folder +import com.anthonyla.paperize.feature.wallpaper.domain.model.Wallpaper +import com.anthonyla.paperize.feature.wallpaper.domain.repository.AlbumRepository +import com.anthonyla.paperize.feature.wallpaper.presentation.add_album_screen.AddAlbumEvent +import com.anthonyla.paperize.feature.wallpaper.presentation.add_album_screen.SelectionState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import javax.inject.Inject +import kotlin.collections.plus + +@HiltViewModel +class AlbumScreenViewModel @Inject constructor( + application: Application, + private val repository: AlbumRepository, +) : AndroidViewModel(application) { + private val context: Context + get() = getApplication().applicationContext + private val _state = MutableStateFlow(AlbumViewState()) + val state = combine( + loadAlbumsFlow(), + _state + ) { albums: List, currentState: AlbumViewState -> + currentState.copy(albums = albums) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = AlbumViewState() + ) + + private fun loadAlbumsFlow() = repository.getAlbumsWithWallpaperAndFolder() + + init { + viewModelScope.launch { + state.collect { newState -> _state.value = newState } + } + } + + fun onEvent(event: AlbumViewEvent) { + when (event) { + is AlbumViewEvent.LoadAlbum -> { + viewModelScope.launch { + _state.update { + it.copy(initialAlbumName = event.initialAlbumName) + } + } + } + + is AlbumViewEvent.AddWallpapers -> { + viewModelScope.launch { + if (event.wallpaperUris.isEmpty()) { return@launch } + val album = _state.value.albums.find { it.album.initialAlbumName == _state.value.initialAlbumName } + if (album == null) { return@launch } + val existingUris = album.wallpapers.map { it.wallpaperUri } + val newUris = event.wallpaperUris.filter { it !in existingUris } + if (newUris.isEmpty()) { return@launch } + val wallpapers = newUris.map { uri -> + val metadata = getImageMetadata(context, uri) + Wallpaper( + initialAlbumName = _state.value.initialAlbumName, + wallpaperUri = uri.compress("content://com.android.externalstorage.documents/"), + fileName = metadata.filename, + dateModified = metadata.lastModified, + order = album.wallpapers.size + 1, + key = _state.value.initialAlbumName.hashCode() + uri.hashCode() + ) + } + repository.upsertWallpaperList(wallpapers) + _state.update { + it.copy( + isEmpty = false, + selectionState = SelectionState(), + isLoading = false + ) + } + } + } + + is AlbumViewEvent.AddFolder -> { + viewModelScope.launch { + val album = _state.value.albums.find { it.album.initialAlbumName == _state.value.initialAlbumName } + if (album == null || album.folders.any { it.folderUri == event.directoryUri }) { return@launch } + _state.update { it.copy(isLoading = true) } + val wallpapers = getWallpaperFromFolder(event.directoryUri, context).map { + it.copy( + initialAlbumName = _state.value.initialAlbumName, + key = _state.value.initialAlbumName.hashCode() + event.directoryUri.hashCode() + it.wallpaperUri.hashCode() + ) + } + val folderName = getFolderNameFromUri(event.directoryUri, context) + val lastModified = getFolderLastModified(event.directoryUri, context) + val folder = Folder( + initialAlbumName = _state.value.initialAlbumName, + folderUri = event.directoryUri, + folderName = folderName, + coverUri = wallpapers.map { it.wallpaperUri }.firstOrNull() ?: "", + dateModified = lastModified, + wallpapers = wallpapers, + order = album.folders.size + 1, + key = _state.value.initialAlbumName.hashCode() + event.directoryUri.hashCode() + ) + repository.upsertFolder(folder) + _state.update { + it.copy( + isEmpty = false, + selectionState = SelectionState(), + isLoading = false + ) + } + } + } + + is AlbumViewEvent.ChangeAlbumName -> { + viewModelScope.launch { + if (!_state.value.albums.any { it.album.displayedAlbumName == event.displayName }) { + val album = _state.value.albums.find { it.album.initialAlbumName == _state.value.initialAlbumName } + if (album == null) { return@launch } + repository.updateAlbum( + album.album.copy(displayedAlbumName = event.displayName) + ) + } + } + } + + is AlbumViewEvent.SelectWallpaper -> { + viewModelScope.launch { + if (!_state.value.selectionState.selectedWallpapers.contains(event.wallpaperUri)) { + val album = _state.value.albums.find { it.album.initialAlbumName == _state.value.initialAlbumName } + if (album == null) { return@launch } + _state.update { + val newSelectedWallpapers = it.selectionState.selectedWallpapers.plus(event.wallpaperUri) + it.copy( + selectionState = it.selectionState.copy( + selectedWallpapers = newSelectedWallpapers, + selectedCount = newSelectedWallpapers.size + it.selectionState.selectedFolders.size, + allSelected = it.selectionState.selectedFolders.size + newSelectedWallpapers.size >= + album.folders.size + album.wallpapers.size + ) + ) + } + } + } + } + + is AlbumViewEvent.SelectFolder -> { + viewModelScope.launch { + if (!_state.value.selectionState.selectedFolders.contains(event.directoryUri)) { + val album = _state.value.albums.find { it.album.initialAlbumName == _state.value.initialAlbumName } + if (album == null) { return@launch } + _state.update { + val newSelectedFolders = it.selectionState.selectedFolders.plus(event.directoryUri) + it.copy( + selectionState = it.selectionState.copy( + selectedFolders = newSelectedFolders, + selectedCount = newSelectedFolders.size + it.selectionState.selectedWallpapers.size, + allSelected = newSelectedFolders.size + it.selectionState.selectedWallpapers.size >= + album.folders.size + album.wallpapers.size + ) + ) + } + } + } + } + + is AlbumViewEvent.DeselectFolder -> { + viewModelScope.launch { + if (_state.value.selectionState.selectedFolders.contains(event.directoryUri)) { + _state.update { + it.copy( + selectionState = it.selectionState.copy( + selectedFolders = it.selectionState.selectedFolders.minus(event.directoryUri), + selectedCount = it.selectionState.selectedCount - 1, + allSelected = false + ) + ) + } + } + } + } + + is AlbumViewEvent.DeselectWallpaper -> { + viewModelScope.launch { + if (_state.value.selectionState.selectedWallpapers.contains(event.wallpaperUri)) { + _state.update { + it.copy( + selectionState = it.selectionState.copy( + selectedWallpapers = it.selectionState.selectedWallpapers.minus(event.wallpaperUri), + selectedCount = it.selectionState.selectedCount - 1, + allSelected = false + ) + ) + } + } + } + } + + is AlbumViewEvent.DeleteSelected -> { + viewModelScope.launch { + val album = _state.value.albums.find { it.album.initialAlbumName == _state.value.initialAlbumName } + if (album == null) { return@launch } + val selectedFolders = _state.value.selectionState.selectedFolders + val selectedWallpapers = _state.value.selectionState.selectedWallpapers + val foldersToDelete = album.folders.filter { selectedFolders.contains(it.folderUri) } + val wallpapersToDelete = album.wallpapers.filter { selectedWallpapers.contains(it.wallpaperUri) } + if (foldersToDelete.size + wallpapersToDelete.size == album.folders.size + album.wallpapers.size) { + repository.cascadeDeleteAlbum(album.album) + } + else { + repository.deleteFolderList(foldersToDelete) + repository.deleteWallpaperList(wallpapersToDelete) + if (album.album.coverUri in (foldersToDelete.map { it.folderUri } + wallpapersToDelete.map { it.wallpaperUri })) { + val newCover = album.folders.minus(foldersToDelete).firstOrNull()?.coverUri ?: album.wallpapers.minus(wallpapersToDelete).firstOrNull()?.wallpaperUri ?: "" + repository.updateAlbum(album.album.copy(coverUri = newCover)) + } + } + _state.update { + it.copy( + selectionState = SelectionState(), + isLoading = false + ) + } + } + } + + is AlbumViewEvent.Reset -> { + viewModelScope.launch { + _state.update { AlbumViewState() } + } + } + + is AlbumViewEvent.SelectAll -> { + viewModelScope.launch { + if (!_state.value.selectionState.allSelected) { + val album = _state.value.albums.find { it.album.initialAlbumName == _state.value.initialAlbumName } + if (album == null) { return@launch } + _state.update { + it.copy( + selectionState = SelectionState( + selectedFolders = album.folders.map { it.folderUri }, + selectedWallpapers = album.wallpapers.map { it.wallpaperUri }, + allSelected = true, + selectedCount = album.folders.size + album.wallpapers.size + ) + ) + } + } + } + } + + is AlbumViewEvent.DeselectAll -> { + viewModelScope.launch { + _state.update { it.copy(selectionState = SelectionState()) } + } + } + + is AlbumViewEvent.SetLoading -> { + viewModelScope.launch { + _state.update { it.copy(isLoading = event.isLoading) } + } + } + + is AlbumViewEvent.LoadFoldersAndWallpapers -> { + viewModelScope.launch { + _state.update { + val album = _state.value.albums.find { it.album.initialAlbumName == _state.value.initialAlbumName } + if (album == null) { return@launch } + repository.upsertAlbumWithWallpaperAndFolder( + AlbumWithWallpaperAndFolder( + album = album.album.copy( + coverUri = event.folders.firstOrNull()?.coverUri ?: event.wallpapers.firstOrNull()?.wallpaperUri ?: "" + ), + folders = event.folders.map { + it.copy(coverUri = it.wallpapers.firstOrNull()?.wallpaperUri ?: "") + }, + wallpapers = event.wallpapers + ) + ) + it.copy( + selectionState = SelectionState(), + isLoading = false + ) + } + } + } + + is AlbumViewEvent.DeleteAlbum -> { + viewModelScope.launch { + val album = _state.value.albums.find { it.album.initialAlbumName == _state.value.initialAlbumName } + if (album == null) { return@launch } + repository.cascadeDeleteAlbum(album.album) + _state.update { AlbumViewState() } + } + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewEvent.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewEvent.kt index 57a34157..7d88442e 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewEvent.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewEvent.kt @@ -1,41 +1,45 @@ package com.anthonyla.paperize.feature.wallpaper.presentation.album_view_screen -import com.anthonyla.paperize.feature.wallpaper.domain.model.AlbumWithWallpaperAndFolder +import com.anthonyla.paperize.feature.wallpaper.domain.model.Folder +import com.anthonyla.paperize.feature.wallpaper.domain.model.Wallpaper +import com.anthonyla.paperize.feature.wallpaper.presentation.add_album_screen.AddAlbumEvent sealed class AlbumViewEvent { - data object ClearState: AlbumViewEvent() - data class SelectAll( - val albumsWithWallpaper: AlbumWithWallpaperAndFolder - ): AlbumViewEvent() + data object Reset: AlbumViewEvent() + data object SelectAll: AlbumViewEvent() data object DeselectAll: AlbumViewEvent() - data class DeleteSelected( - val albumsWithWallpaper: AlbumWithWallpaperAndFolder - ): AlbumViewEvent() + data object DeleteSelected: AlbumViewEvent() + data object DeleteAlbum: AlbumViewEvent() + data class LoadAlbum( + val initialAlbumName: String + ): AlbumViewEvent() data class AddWallpapers( - val album: AlbumWithWallpaperAndFolder, val wallpaperUris: List ): AlbumViewEvent() - data class AddFolder( - val album: AlbumWithWallpaperAndFolder, val directoryUri: String ): AlbumViewEvent() - data class SelectWallpaper( val wallpaperUri: String ): AlbumViewEvent() data class SelectFolder( val directoryUri: String ): AlbumViewEvent() - - data class RemoveFolderFromSelection( + data class DeselectFolder( val directoryUri: String ): AlbumViewEvent() - data class RemoveWallpaperFromSelection( + data class DeselectWallpaper( val wallpaperUri: String ): AlbumViewEvent() - data class SetSize( - val size: Int + data class ChangeAlbumName( + val displayName: String + ): AlbumViewEvent() + data class SetLoading( + val isLoading: Boolean + ): AlbumViewEvent() + data class LoadFoldersAndWallpapers( + val folders: List, + val wallpapers: List ): AlbumViewEvent() } \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewScreen.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewScreen.kt index ebadf563..c8eb2084 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewScreen.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewScreen.kt @@ -2,7 +2,6 @@ package com.anthonyla.paperize.feature.wallpaper.presentation.album_view_screen import android.content.Intent import android.net.Uri -import android.util.Log import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @@ -10,64 +9,81 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.anthonyla.paperize.feature.wallpaper.domain.model.AlbumWithWallpaperAndFolder import com.anthonyla.paperize.feature.wallpaper.domain.model.Folder +import com.anthonyla.paperize.feature.wallpaper.domain.model.Wallpaper +import com.anthonyla.paperize.feature.wallpaper.presentation.add_album_screen.AddAlbumEvent import com.anthonyla.paperize.feature.wallpaper.presentation.add_album_screen.components.AddAlbumAnimatedFab import com.anthonyla.paperize.feature.wallpaper.presentation.album.components.FolderItem import com.anthonyla.paperize.feature.wallpaper.presentation.album.components.WallpaperItem import com.anthonyla.paperize.feature.wallpaper.presentation.album_view_screen.components.AlbumViewTopBar +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import my.nanihadesuka.compose.LazyVerticalGridScrollbar import my.nanihadesuka.compose.ScrollbarSettings @Composable fun AlbumViewScreen( - albumViewScreenViewModel: AlbumViewScreenViewModel = hiltViewModel(), - album: AlbumWithWallpaperAndFolder, + albumScreenViewModel: AlbumScreenViewModel, animate: Boolean, onBackClick: () -> Unit, onShowWallpaperView: (String) -> Unit, onShowFolderView: (Folder) -> Unit, onDeleteAlbum: () -> Unit, - onAlbumNameChange: (String, AlbumWithWallpaperAndFolder) -> Unit, - onSelectionDeleted: () -> Unit, + onSortClick: (List, List) -> Unit, ) { - albumViewScreenViewModel.onEvent(AlbumViewEvent.SetSize(album.wallpapers.size + album.folders.size)) // For selectedAll state - val lazyListState = rememberLazyGridState() - val albumState = albumViewScreenViewModel.state.collectAsStateWithLifecycle() - var selectionMode by rememberSaveable { mutableStateOf(false) } val context = LocalContext.current + val scope = rememberCoroutineScope() + val lazyListState = rememberLazyGridState() + val albumViewState = albumScreenViewModel.state.collectAsStateWithLifecycle() + val album = albumViewState.value.albums.find { it.album.initialAlbumName == albumViewState.value.initialAlbumName } + + BackHandler( + enabled = albumViewState.value.selectionState.selectedCount > 0, + onBack = { + if (!albumViewState.value.isLoading) { + albumScreenViewModel.onEvent(AlbumViewEvent.DeselectAll) + } + } + ) /** Image picker **/ val imagePickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.OpenMultipleDocuments(), onResult = { uris: List -> - val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION - val uriList = uris.mapNotNull { uri -> - context.contentResolver.takePersistableUriPermission(uri, takeFlags) - val persistedUriPermissions = context.contentResolver.persistedUriPermissions - if (persistedUriPermissions.any { it.uri == uri }) uri.toString() else null - } - if (uriList.isNotEmpty()) { - albumViewScreenViewModel.onEvent(AlbumViewEvent.AddWallpapers(album, uriList)) + scope.launch(Dispatchers.IO) { + albumScreenViewModel.onEvent(AlbumViewEvent.SetLoading(true)) + val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION + val uriList = uris.mapNotNull { uri -> + context.contentResolver.takePersistableUriPermission(uri, takeFlags) + val persistedUriPermissions = context.contentResolver.persistedUriPermissions + if (persistedUriPermissions.any { it.uri == uri }) uri.toString() else null + } + if (uriList.isNotEmpty()) { + withContext(Dispatchers.Main) { + albumScreenViewModel.onEvent(AlbumViewEvent.AddWallpapers(uriList)) + } + } + albumScreenViewModel.onEvent(AlbumViewEvent.SetLoading(false)) } } ) @@ -76,135 +92,161 @@ fun AlbumViewScreen( val folderPickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.OpenDocumentTree(), onResult = { uri: Uri? -> - val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION - if (uri != null) { - context.contentResolver.takePersistableUriPermission(uri, takeFlags) - val persistedUriPermissions = context.contentResolver.persistedUriPermissions - if (persistedUriPermissions.any { it.uri == uri }) { - albumViewScreenViewModel.onEvent(AlbumViewEvent.AddFolder(album, uri.toString())) + scope.launch(Dispatchers.IO) { + val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION + if (uri != null) { + context.contentResolver.takePersistableUriPermission(uri, takeFlags) + val persistedUriPermissions = context.contentResolver.persistedUriPermissions + if (persistedUriPermissions.any { it.uri == uri }) { + albumScreenViewModel.onEvent(AlbumViewEvent.AddFolder(uri.toString())) + } } } } ) - BackHandler( - enabled = selectionMode, - onBack = { - albumViewScreenViewModel.onEvent(AlbumViewEvent.DeselectAll) - selectionMode = false - } - ) - Scaffold( topBar = { AlbumViewTopBar( - title = album.album.displayedAlbumName, - selectionMode = selectionMode, - albumState = albumViewScreenViewModel.state, + title = album?.album?.displayedAlbumName ?: "" , + allSelected = albumViewState.value.selectionState.allSelected, + selectedCount = albumViewState.value.selectionState.selectedCount, + selectionMode = albumViewState.value.selectionState.selectedCount > 0, + isLoad = albumViewState.value.isLoading, + onSelectAllClick = { + if (!albumViewState.value.isLoading) { + if (!albumViewState.value.selectionState.allSelected) albumScreenViewModel.onEvent( + AlbumViewEvent.SelectAll + ) + else albumScreenViewModel.onEvent(AlbumViewEvent.DeselectAll) + } + }, onBackClick = { - albumViewScreenViewModel.onEvent(AlbumViewEvent.ClearState) + albumScreenViewModel.onEvent(AlbumViewEvent.Reset) onBackClick() }, - onDeleteAlbum = onDeleteAlbum, - onTitleChange = { onAlbumNameChange(it, album) }, - onSelectAllClick = { - if (!albumState.value.allSelected) albumViewScreenViewModel.onEvent(AlbumViewEvent.SelectAll(album)) - else albumViewScreenViewModel.onEvent(AlbumViewEvent.DeselectAll) - }, onDeleteSelected = { - selectionMode = false - albumViewScreenViewModel.onEvent(AlbumViewEvent.DeleteSelected(album)) - albumViewScreenViewModel.onEvent(AlbumViewEvent.DeselectAll) - onSelectionDeleted() + if (!albumViewState.value.isLoading) { + if (album != null) { + if (album.folders.size + album.wallpapers.size == albumViewState.value.selectionState.selectedCount) { + onBackClick() + } + albumScreenViewModel.onEvent(AlbumViewEvent.DeleteSelected) + } + } + }, + onDeleteAlbum = { + if (!albumViewState.value.isLoading && album != null) { + albumScreenViewModel.onEvent(AlbumViewEvent.DeleteAlbum) + onDeleteAlbum() + } + }, + onTitleChange = { + if (!albumViewState.value.isLoading && album != null) { + albumScreenViewModel.onEvent(AlbumViewEvent.ChangeAlbumName(it)) + } + }, + onSortClick = { + if (!albumViewState.value.isLoading && album != null) { + onSortClick(album.folders, album.wallpapers) + } } ) }, floatingActionButton = { AddAlbumAnimatedFab( - isLoading = false, + isLoading = albumViewState.value.isLoading, animate = animate, onImageClick = { - selectionMode = false + albumScreenViewModel.onEvent(AlbumViewEvent.DeselectAll) imagePickerLauncher.launch(arrayOf("image/*")) }, onFolderClick = { - selectionMode = false + albumScreenViewModel.onEvent(AlbumViewEvent.DeselectAll) folderPickerLauncher.launch(null) } ) }, + bottomBar = { + if (albumViewState.value.isLoading) { + LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) + } + }, content = { it -> - LazyVerticalGridScrollbar( - state = lazyListState, - settings = ScrollbarSettings.Default.copy( - thumbUnselectedColor = MaterialTheme.colorScheme.primary, - thumbSelectedColor = MaterialTheme.colorScheme.primary, - thumbShape = RoundedCornerShape(16.dp), - scrollbarPadding = 1.dp, - ), - modifier = Modifier - .fillMaxSize() - .padding(it), - ) { - LazyVerticalGrid( + if (album != null) { + LazyVerticalGridScrollbar( state = lazyListState, - modifier = Modifier.fillMaxSize(), - columns = GridCells.Adaptive(150.dp), - contentPadding = PaddingValues(4.dp, 4.dp), - horizontalArrangement = Arrangement.Start, + settings = ScrollbarSettings.Default.copy( + thumbUnselectedColor = MaterialTheme.colorScheme.primary, + thumbSelectedColor = MaterialTheme.colorScheme.primary, + thumbShape = RoundedCornerShape(16.dp), + scrollbarPadding = 1.dp, + ), + modifier = Modifier + .fillMaxSize() + .padding(it), ) { - items(count = album.folders.size, key = { index -> album.folders[index].folderUri }) { index -> - FolderItem( - folder = album.folders[index], - itemSelected = albumState.value.selectedFolders.contains(album.folders[index].folderUri), - selectionMode = selectionMode, - onItemSelection = { - albumViewScreenViewModel.onEvent( - if (!albumState.value.selectedFolders.contains(album.folders[index].folderUri)) - AlbumViewEvent.SelectFolder(album.folders[index].folderUri) - else { - AlbumViewEvent.RemoveFolderFromSelection(album.folders[index].folderUri) + LazyVerticalGrid( + state = lazyListState, + modifier = Modifier.fillMaxSize(), + columns = GridCells.Adaptive(150.dp), + contentPadding = PaddingValues(4.dp, 4.dp), + horizontalArrangement = Arrangement.Start, + ) { + items(count = album.folders.size, key = { index -> album.folders[index].folderUri }) { index -> + FolderItem( + folder = album.folders[index], + itemSelected = albumViewState.value.selectionState.selectedFolders.contains(album.folders[index].folderUri), + selectionMode = albumViewState.value.selectionState.selectedCount > 0, + onItemSelection = { + if (!albumViewState.value.isLoading) { + albumScreenViewModel.onEvent( + if (!albumViewState.value.selectionState.selectedFolders.contains(album.folders[index].folderUri)) + AlbumViewEvent.SelectFolder(album.folders[index].folderUri) + else { + AlbumViewEvent.DeselectFolder(album.folders[index].folderUri) + } + ) } - ) - }, - onFolderViewClick = { - onShowFolderView(album.folders[index]) - }, - modifier = Modifier - .padding(4.dp) - .size(150.dp, 350.dp) - ) - } - items(count = album.wallpapers.size, key = { index -> album.wallpapers[index].wallpaperUri }) { index -> - Column { + }, + onFolderViewClick = { + if (!albumViewState.value.isLoading) { + onShowFolderView(album.folders[index]) + } + }, + modifier = Modifier + .padding(4.dp) + .size(150.dp, 350.dp) + ) + } + items(count = album.wallpapers.size, key = { index -> album.wallpapers[index].wallpaperUri }) { index -> WallpaperItem( wallpaperUri = album.wallpapers[index].wallpaperUri, - itemSelected = albumState.value.selectedWallpapers.contains(album.wallpapers[index].wallpaperUri), - selectionMode = selectionMode, + itemSelected = albumViewState.value.selectionState.selectedWallpapers.contains(album.wallpapers[index].wallpaperUri), + selectionMode = albumViewState.value.selectionState.selectedCount > 0, onItemSelection = { - albumViewScreenViewModel.onEvent( - if (!albumState.value.selectedWallpapers.contains(album.wallpapers[index].wallpaperUri)) - AlbumViewEvent.SelectWallpaper(album.wallpapers[index].wallpaperUri) - else { - AlbumViewEvent.RemoveWallpaperFromSelection(album.wallpapers[index].wallpaperUri) - } - ) + if (!albumViewState.value.isLoading) { + albumScreenViewModel.onEvent( + if (!albumViewState.value.selectionState.selectedWallpapers.contains( + album.wallpapers[index].wallpaperUri + ) + ) AlbumViewEvent.SelectWallpaper(album.wallpapers[index].wallpaperUri) + else AlbumViewEvent.DeselectWallpaper(album.wallpapers[index].wallpaperUri) + ) + } }, onWallpaperViewClick = { - onShowWallpaperView(album.wallpapers[index].wallpaperUri) + if (!albumViewState.value.isLoading) + onShowWallpaperView(album.wallpapers[index].wallpaperUri) }, modifier = Modifier .padding(4.dp) .size(150.dp, 350.dp) ) - Text( - text = album.wallpapers[index].fileName, - modifier = Modifier.padding(4.dp) - ) } } - } + } } } ) diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewScreenViewModel.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewScreenViewModel.kt deleted file mode 100644 index a8fb3f81..00000000 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewScreenViewModel.kt +++ /dev/null @@ -1,225 +0,0 @@ -package com.anthonyla.paperize.feature.wallpaper.presentation.album_view_screen - - -import android.R.attr.order -import android.app.Application -import android.content.Context -import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.viewModelScope -import com.anthonyla.paperize.core.getFolderNameFromUri -import com.anthonyla.paperize.core.getWallpaperFromFolder -import com.anthonyla.paperize.feature.wallpaper.domain.model.Folder -import com.anthonyla.paperize.feature.wallpaper.domain.model.Wallpaper -import com.anthonyla.paperize.feature.wallpaper.domain.repository.AlbumRepository -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class AlbumViewScreenViewModel @Inject constructor( - application: Application, - private val repository: AlbumRepository, -) : AndroidViewModel(application) { - private val context: Context - get() = getApplication().applicationContext - private val _state = MutableStateFlow(AlbumViewState()) - val state = _state.stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(5000), AlbumViewState() - ) - - fun onEvent(event: AlbumViewEvent) { - when (event) { - is AlbumViewEvent.DeleteSelected -> { - viewModelScope.launch { - event.albumsWithWallpaper.let { album -> - val folders = album.folders.filter { _state.value.selectedFolders.contains(it.folderUri)} - val wallpapers = album.wallpapers.filter { _state.value.selectedWallpapers.contains(it.wallpaperUri)} - val containsCoverUri = wallpapers.any { it.wallpaperUri == album.album.coverUri } - repository.deleteFolderList(folders) - repository.deleteWallpaperList(wallpapers) - if (containsCoverUri) { - repository.updateAlbum(album.album.copy(coverUri = null)) - } - } - } - } - is AlbumViewEvent.SelectAll -> { - viewModelScope.launch { - event.albumsWithWallpaper.let { album -> - if (!_state.value.allSelected) { - _state.update { it -> - it.copy( - selectedFolders = album.folders.map { it.folderUri }, - selectedWallpapers = album.wallpapers.map { it.wallpaperUri }, - selectedCount = album.folders.size + album.wallpapers.size, - maxSize = album.folders.size + album.wallpapers.size, - allSelected = true - ) - } - } - } - } - } - - is AlbumViewEvent.DeselectAll -> { - viewModelScope.launch { - _state.update { - it.copy( - selectedFolders = emptyList(), - selectedWallpapers = emptyList(), - selectedCount = 0, - allSelected = false - ) - } - } - } - - is AlbumViewEvent.SelectFolder -> { - viewModelScope.launch { - if (!_state.value.selectedFolders.any { it == event.directoryUri }) { - _state.update { - val folders = it.selectedFolders.plus(event.directoryUri) - it.copy( - selectedFolders = folders, - selectedCount = it.selectedCount + 1, - allSelected = folders.size + it.selectedWallpapers.size >= it.maxSize - ) - } - } - } - } - - is AlbumViewEvent.SelectWallpaper -> { - viewModelScope.launch { - if (!_state.value.selectedWallpapers.any { it == event.wallpaperUri }) { - _state.update { - val wallpapers = it.selectedWallpapers.plus(event.wallpaperUri) - it.copy( - selectedWallpapers = it.selectedWallpapers.plus(event.wallpaperUri), - selectedCount = it.selectedCount + 1, - allSelected = it.selectedFolders.size + wallpapers.size >= it.maxSize - ) - } - } - } - } - - is AlbumViewEvent.RemoveFolderFromSelection -> { - viewModelScope.launch { - if (_state.value.selectedFolders.find { it == event.directoryUri } != null) { - _state.update { - val folders = it.selectedFolders.minus(event.directoryUri) - it.copy( - selectedFolders = folders, - selectedCount = it.selectedCount - 1, - allSelected = folders.size + it.selectedWallpapers.size >= it.maxSize - ) - } - } - } - } - - is AlbumViewEvent.RemoveWallpaperFromSelection -> { - viewModelScope.launch { - if (_state.value.selectedWallpapers.find { it == event.wallpaperUri } != null) { - _state.update { - val wallpapers = it.selectedWallpapers.minus(event.wallpaperUri) - it.copy( - selectedWallpapers = wallpapers, - selectedCount = it.selectedCount - 1, - allSelected = it.selectedFolders.size + wallpapers.size >= it.maxSize - ) - } - } - } - } - - is AlbumViewEvent.ClearState -> { - viewModelScope.launch { - _state.update { - it.copy( - selectedFolders = emptyList(), - selectedWallpapers = emptyList(), - allSelected = false, - selectedCount = 0, - maxSize = 0 - ) - } - } - } - - is AlbumViewEvent.SetSize -> { - viewModelScope.launch { - _state.update { - it.copy( - maxSize = event.size - ) - } - } - } - - is AlbumViewEvent.AddWallpapers -> { - viewModelScope.launch { - val wallpaperUris = event.wallpaperUris.filterNot { it in event.album.wallpapers.map { wallpaper -> wallpaper.wallpaperUri } } - val wallpapers = wallpaperUris.map { uri -> - Wallpaper( - initialAlbumName = event.album.album.initialAlbumName, - wallpaperUri = uri, - key = uri.hashCode() + event.album.album.initialAlbumName.hashCode(), - fileName = "", - order = 0, - dateModified = 0 - ) - } - repository.upsertWallpaperList(wallpapers) - _state.update { - it.copy( - selectedFolders = emptyList(), - selectedWallpapers = emptyList(), - allSelected = false, - selectedCount = 0, - maxSize = it.maxSize + wallpapers.size - ) - } - } - } - - is AlbumViewEvent.AddFolder -> { - viewModelScope.launch { - if (event.directoryUri !in event.album.folders.map { it.folderUri }) { - val wallpapers: List = getWallpaperFromFolder(event.directoryUri, context) - if (wallpapers.isEmpty()) { return@launch } - val folderName = getFolderNameFromUri(event.directoryUri, context) - repository.upsertFolder( - Folder( - folderUri = event.directoryUri, - folderName = folderName, - initialAlbumName = event.album.album.initialAlbumName, - key = event.directoryUri.hashCode() + event.album.album.initialAlbumName.hashCode(), - coverUri = wallpapers.randomOrNull()?.wallpaperUri ?: "", - wallpapers = wallpapers, - order = 0, - dateModified = 0 - ) - ) - _state.update { - it.copy( - selectedFolders = emptyList(), - selectedWallpapers = emptyList(), - allSelected = false, - selectedCount = 0, - maxSize = it.maxSize + 1 - ) - } - } - } - } - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewState.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewState.kt index b246c8be..e8a2650f 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewState.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/AlbumViewState.kt @@ -1,9 +1,11 @@ package com.anthonyla.paperize.feature.wallpaper.presentation.album_view_screen +import com.anthonyla.paperize.feature.wallpaper.domain.model.AlbumWithWallpaperAndFolder +import com.anthonyla.paperize.feature.wallpaper.presentation.add_album_screen.SelectionState data class AlbumViewState ( - val selectedWallpapers: List = emptyList(), - val selectedFolders: List = emptyList(), - val allSelected: Boolean = false, - val selectedCount: Int = 0, - val maxSize: Int = 0 + val albums: List = emptyList(), + val initialAlbumName: String = "", + val selectionState: SelectionState = SelectionState(), + val isEmpty: Boolean = true, + val isLoading: Boolean = false ) \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/components/AlbumViewTopBar.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/components/AlbumViewTopBar.kt index bc997ea2..6832a6d9 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/components/AlbumViewTopBar.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/album_view_screen/components/AlbumViewTopBar.kt @@ -3,6 +3,7 @@ package com.anthonyla.paperize.feature.wallpaper.presentation.album_view_screen. import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize @@ -10,7 +11,9 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.Sort import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.RadioButtonUnchecked import androidx.compose.material.icons.outlined.Delete @@ -47,16 +50,18 @@ import kotlinx.coroutines.flow.StateFlow @OptIn(ExperimentalMaterial3Api::class) @Composable fun AlbumViewTopBar( - albumState: StateFlow, title: String, - onBackClick: () -> Unit, + allSelected: Boolean, + selectedCount: Int, selectionMode: Boolean, + isLoad: Boolean, + onBackClick: () -> Unit, + onSortClick: () -> Unit, onSelectAllClick: () -> Unit, onDeleteAlbum: () -> Unit, onDeleteSelected: () -> Unit, onTitleChange: (String) -> Unit, ) { - val state = albumState.collectAsStateWithLifecycle() var showDeleteAlertDialog by rememberSaveable { mutableStateOf(false) } var showNameChangeDialog by rememberSaveable { mutableStateOf(false) } var menuExpanded by rememberSaveable { mutableStateOf(false) } @@ -75,38 +80,34 @@ fun AlbumViewTopBar( ) { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = stringResource(id = R.string.home_screen), + contentDescription = stringResource(id = R.string.home_screen) ) } - } - else { - Row ( + } else { + Row( horizontalArrangement = Arrangement.Start, verticalAlignment = Alignment.CenterVertically ) { - IconButton( - onClick = onSelectAllClick - ) { - val bgColor = MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp) - if (state.value.allSelected) { - Icon( - imageVector = Icons.Default.CheckCircle, - contentDescription = stringResource(R.string.all_images_selected_for_deletion), - modifier = Modifier + val bgColor = MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp) + IconButton(onClick = onSelectAllClick) { + Icon( + imageVector = if (allSelected) Icons.Default.CheckCircle else Icons.Default.RadioButtonUnchecked, + contentDescription = stringResource( + if (allSelected) R.string.all_images_selected_for_deletion + else R.string.select_all_images_for_deletion + ), + modifier = if (allSelected) { + Modifier .padding(4.dp) .border(2.dp, bgColor, CircleShape) .clip(CircleShape) .background(bgColor) - ) - } else { - Icon( - imageVector = Icons.Default.RadioButtonUnchecked, - contentDescription = stringResource(R.string.select_all_images_for_deletion), - modifier = Modifier.padding(6.dp) - ) - } + } else { + Modifier.padding(6.dp) + } + ) } - Text(stringResource(R.string.selected_count, state.value.selectedCount)) + Text(stringResource(R.string.selected, selectedCount)) } } }, @@ -116,17 +117,32 @@ fun AlbumViewTopBar( onDismissRequest = { showDeleteAlertDialog = false }, onConfirmation = { showDeleteAlertDialog = false - onDeleteAlbum() + if (!isLoad) { onDeleteAlbum() } } ) if (showNameChangeDialog) AlbumNameDialog ( onDismissRequest = { showNameChangeDialog = false }, onConfirmation = { showNameChangeDialog = false - onTitleChange(it) + if (!isLoad) { onTitleChange(it) } } ) - IconButton(onClick = { menuExpanded = true }) { + Row { + Box { + IconButton( + onClick = { if (!isLoad) { onSortClick() } } + ) { + Icon( + imageVector = Icons.AutoMirrored.Default.Sort, + contentDescription = stringResource(R.string.sort_items), + modifier = Modifier.padding(6.dp) + ) + } + } + } + IconButton(onClick = { + if (!isLoad) { menuExpanded = true } + }) { Icon( imageVector = Icons.Filled.MoreVert, contentDescription = stringResource(R.string.more_options), @@ -144,14 +160,18 @@ fun AlbumViewTopBar( DropdownMenuItem( text = { Text(stringResource(R.string.delete_album)) }, onClick = { - showDeleteAlertDialog = true - menuExpanded = false + if (!isLoad) { + showDeleteAlertDialog = true + } + menuExpanded = false } ) DropdownMenuItem( text = { Text(stringResource(R.string.change_name)) }, onClick = { - showNameChangeDialog = true + if (!isLoad) { + showNameChangeDialog = true + } menuExpanded = false } ) @@ -163,14 +183,16 @@ fun AlbumViewTopBar( onDismissRequest = { showSelectionDeleteDialog = false }, onConfirmation = { showSelectionDeleteDialog = false - onDeleteSelected() + if (!isLoad) { + onDeleteSelected() + } } ) IconButton( onClick = { showSelectionDeleteDialog = true } ) { Icon( - imageVector = Icons.Outlined.Delete, + imageVector = if (showSelectionDeleteDialog) Icons.Filled.Delete else Icons.Outlined.Delete, contentDescription = stringResource(R.string.select_all_images_for_deletion), modifier = Modifier.padding(6.dp) ) diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/sort_view_screen/SortViewModel.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/sort_view_screen/SortViewModel.kt index 1263d205..aa7f2d71 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/sort_view_screen/SortViewModel.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/sort_view_screen/SortViewModel.kt @@ -52,7 +52,10 @@ class SortViewModel @Inject constructor (): ViewModel() { } val updatedFolders = state.value.folders.map { folder -> if (folder.folderUri == event.folderId) { - folder.copy(wallpapers = currentWallpapers) + folder.copy( + wallpapers = currentWallpapers, + coverUri = currentWallpapers.firstOrNull()?.wallpaperUri ?: folder.coverUri + ) } else { folder } diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/sort_view_screen/SortViewScreen.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/sort_view_screen/SortViewScreen.kt index 9f09b253..1f2c33d3 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/sort_view_screen/SortViewScreen.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/sort_view_screen/SortViewScreen.kt @@ -25,15 +25,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.anthonyla.paperize.feature.wallpaper.presentation.sort_view_screen.components.SortViewTopBar import sh.calvin.reorderable.ReorderableItem import sh.calvin.reorderable.rememberReorderableLazyListState -import androidx.compose.foundation.layout.Column import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.res.stringResource import com.anthonyla.paperize.R import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.SharedTransitionLayout -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.size @@ -43,7 +39,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Close -import androidx.compose.material3.Button import androidx.compose.material3.CardColors import androidx.compose.material3.IconButton import androidx.compose.material3.Icon @@ -51,11 +46,9 @@ import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.ui.Alignment import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.IntSize import androidx.core.net.toUri -import com.skydoves.landscapist.ImageOptions +import com.anthonyla.paperize.core.decompress import com.skydoves.landscapist.glide.GlideImage @@ -262,7 +255,7 @@ fun SortViewScreen( ) Spacer(modifier = Modifier.width(8.dp)) GlideImage( - imageModel = { wallpaper.wallpaperUri.toUri() }, + imageModel = { wallpaper.wallpaperUri.decompress("content://com.android.externalstorage.documents/").toUri() }, modifier = Modifier .size(48.dp) .clip(CircleShape) @@ -289,7 +282,7 @@ fun SortViewScreen( modifier = Modifier.fillMaxWidth(), leadingContent = { GlideImage( - imageModel = { currentFolder.coverUri?.toUri() }, + imageModel = { currentFolder.coverUri?.decompress("content://com.android.externalstorage.documents/")?.toUri() }, modifier = Modifier .size(48.dp) .clip(RoundedCornerShape(16.dp)) @@ -368,7 +361,7 @@ fun SortViewScreen( ) Spacer(modifier = Modifier.width(8.dp)) GlideImage( - imageModel = { wallpaper.wallpaperUri.toUri() }, + imageModel = { wallpaper.wallpaperUri.decompress("content://com.android.externalstorage.documents/").toUri() }, modifier = Modifier .size(48.dp) .clip(CircleShape) diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperEvent.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperEvent.kt deleted file mode 100644 index 8ee55fe6..00000000 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperEvent.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.anthonyla.paperize.feature.wallpaper.presentation.wallpaper_screen - -import com.anthonyla.paperize.feature.wallpaper.domain.model.AlbumWithWallpaperAndFolder - -sealed class WallpaperEvent { - data class AddSelectedAlbum(val album: AlbumWithWallpaperAndFolder, val deselectAlbumName: String? = null): WallpaperEvent() - data class RemoveSelectedAlbum(val deselectAlbumName: String): WallpaperEvent() - data object Reset : WallpaperEvent() -} \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperScreen.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperScreen.kt index aedec632..98bb5410 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperScreen.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperScreen.kt @@ -5,7 +5,6 @@ import android.content.Intent import android.os.Build import android.provider.Settings import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -83,15 +82,41 @@ fun WallpaperScreen( ) { val shouldShowScreen = wallpaperSettings.setHomeWallpaper || wallpaperSettings.setLockWallpaper val shouldShowSettings = shouldShowScreen && homeSelectedAlbum != null && lockSelectedAlbum != null - val scrollState = rememberScrollState() val context = LocalContext.current - var openBottomSheet by rememberSaveable { mutableStateOf(false) } val scope = rememberCoroutineScope() + + val scrollState = rememberScrollState() val snackbarHostState = remember { SnackbarHostState() } + var openBottomSheet by rememberSaveable { mutableStateOf(false) } val showInterval = rememberSaveable { mutableStateOf(false) } val lockEnabled = rememberSaveable { mutableStateOf(false) } val homeEnabled = rememberSaveable { mutableStateOf(false) } + val handleAlbumSelection: (AlbumWithWallpaperAndFolder) -> Unit = { album -> + openBottomSheet = false + val alarmManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + ContextCompat.getSystemService(context, AlarmManager::class.java) + } else null + + when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && alarmManager?.canScheduleExactAlarms() == false -> { + context.startActivity(Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)) + } + else -> onSelectAlbum(album, lockEnabled.value, homeEnabled.value) + } + } + + val showSnackBar: (String) -> Unit = { message -> + scope.launch { + snackbarHostState.currentSnackbarData?.dismiss() + snackbarHostState.showSnackbar( + message = message, + actionLabel = context.getString(R.string.dismiss), + duration = SnackbarDuration.Short + ) + } + } + Scaffold( snackbarHost = { SnackbarHost( @@ -99,203 +124,165 @@ fun WallpaperScreen( snackbar = { data -> Snackbar( snackbarData = data, - modifier = Modifier.padding(PaddingValues(horizontal = 16.dp, vertical = 24.dp)), + modifier = Modifier.padding(horizontal = 16.dp, vertical = 24.dp), shape = RoundedCornerShape(24.dp) ) } - ) }, - modifier = Modifier.fillMaxSize(), - content = { padding -> - Column( - modifier = Modifier - .verticalScroll(scrollState) - .fillMaxSize() - .padding(8.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - ChangerSelectionRow( - onHomeCheckedChange = onHomeCheckedChange, - onLockCheckedChange = onLockCheckedChange, - homeEnabled = wallpaperSettings.setHomeWallpaper, - lockEnabled = wallpaperSettings.setLockWallpaper + ) + }, + modifier = Modifier.fillMaxSize() + ) { it + Column ( + modifier = Modifier + .fillMaxSize() + .padding(8.dp) + .verticalScroll(scrollState), + horizontalAlignment = Alignment.CenterHorizontally + ) { + ChangerSelectionRow( + onHomeCheckedChange = onHomeCheckedChange, + onLockCheckedChange = onLockCheckedChange, + homeEnabled = wallpaperSettings.setHomeWallpaper, + lockEnabled = wallpaperSettings.setLockWallpaper + ) + + if (wallpaperSettings.setHomeWallpaper && wallpaperSettings.setLockWallpaper) { + IndividualSchedulingAndToggleRow( + onToggleChanger = onToggleChanger, + onScheduleSeparatelyChange = onScheduleSeparatelyChange, + scheduleSeparately = scheduleSettings.scheduleSeparately, + enableChanger = wallpaperSettings.enableChanger, + animate = themeSettings.animate ) - if (wallpaperSettings.setHomeWallpaper && wallpaperSettings.setLockWallpaper) { - IndividualSchedulingAndToggleRow( - onToggleChanger = onToggleChanger, - onScheduleSeparatelyChange = onScheduleSeparatelyChange, - scheduleSeparately = scheduleSettings.scheduleSeparately, - enableChanger = wallpaperSettings.enableChanger, - animate = themeSettings.animate + } + + if (shouldShowScreen) { + CurrentSelectedAlbum( + homeSelectedAlbum = homeSelectedAlbum, + lockSelectedAlbum = lockSelectedAlbum, + onToggleChanger = { + if (!it) showSnackBar(context.getString(R.string.wallpaper_changer_has_been_disabled)) + onToggleChanger(it) + }, + onOpenBottomSheet = { changeLock, changeHome -> + if (albums.firstOrNull() != null) { + openBottomSheet = true + lockEnabled.value = changeLock + homeEnabled.value = changeHome + } else { + showSnackBar(context.getString(R.string.no_albums_found)) + } + }, + onDeselect = { lock, home -> + if (homeSelectedAlbum != null || lockSelectedAlbum != null) { + val albumName = when { + home && lock -> homeSelectedAlbum?.album?.displayedAlbumName + home -> homeSelectedAlbum?.album?.displayedAlbumName + lock -> lockSelectedAlbum?.album?.displayedAlbumName + else -> "" + } ?: "" + showSnackBar(context.getString(R.string.has_been_unselected, albumName)) + onStop(lock, home) + } + }, + scheduleSeparately = scheduleSettings.scheduleSeparately, + enableChanger = wallpaperSettings.enableChanger, + animate = themeSettings.animate + ) + + if (shouldShowSettings) { + WallpaperPreviewAndScale( + currentHomeWallpaper = wallpaperSettings.currentHomeWallpaper, + currentLockWallpaper = wallpaperSettings.currentLockWallpaper, + scaling = wallpaperSettings.wallpaperScaling, + onScalingChange = onScalingChange, + homeBlurPercentage = effectSettings.homeBlurPercentage, + lockBlurPercentage = effectSettings.lockBlurPercentage, + homeDarkenPercentage = effectSettings.homeDarkenPercentage, + lockDarkenPercentage = effectSettings.lockDarkenPercentage, + homeVignettePercentage = effectSettings.homeVignettePercentage, + lockVignettePercentage = effectSettings.lockVignettePercentage, + homeGrayscalePercentage = effectSettings.homeGrayscalePercentage, + lockGrayscalePercentage = effectSettings.lockGrayscalePercentage, + homeEnabled = wallpaperSettings.setHomeWallpaper, + lockEnabled = wallpaperSettings.setLockWallpaper, + darken = effectSettings.darken, + blur = effectSettings.blur, + vignette = effectSettings.vignette, + grayscale = effectSettings.grayscale ) - } - if (wallpaperSettings.setHomeWallpaper || wallpaperSettings.setLockWallpaper) { - CurrentSelectedAlbum( - homeSelectedAlbum = homeSelectedAlbum, - lockSelectedAlbum = lockSelectedAlbum, - onToggleChanger = { - scope.launch { - snackbarHostState.currentSnackbarData?.dismiss() - if (!it) { - snackbarHostState.showSnackbar( - message = context.getString(R.string.wallpaper_changer_has_been_disabled), - actionLabel = context.getString(R.string.dismiss), - duration = SnackbarDuration.Short - ) - } - } - onToggleChanger(it) - }, - onOpenBottomSheet = { changeLock, changeHome -> - if (albums.firstOrNull() != null) { - openBottomSheet = true - lockEnabled.value = changeLock - homeEnabled.value = changeHome - } else { - scope.launch { - snackbarHostState.currentSnackbarData?.dismiss() - snackbarHostState.showSnackbar( - message = context.getString(R.string.no_albums_found), - actionLabel = context.getString(R.string.dismiss), - duration = SnackbarDuration.Short - ) - } - } + CurrentAndNextChange(scheduleSettings.lastSetTime, scheduleSettings.nextSetTime) + TimeSliders( + homeInterval = scheduleSettings.homeInterval, + lockInterval = scheduleSettings.lockInterval, + startingTime = scheduleSettings.startTime, + onHomeIntervalChange = { days, hours, minutes -> + val totalMinutes = 24 * days * 60 + hours * 60 + minutes + onHomeTimeChange(totalMinutes) }, - onStop = { lock, home -> - if (homeSelectedAlbum != null || lockSelectedAlbum != null) { - scope.launch { - snackbarHostState.currentSnackbarData?.dismiss() - snackbarHostState.showSnackbar( - message = context.getString( - R.string.has_been_unselected, - when { - home && lock -> homeSelectedAlbum?.album?.displayedAlbumName ?: "" - home -> homeSelectedAlbum?.album?.displayedAlbumName ?: "" - lock -> lockSelectedAlbum?.album?.displayedAlbumName ?: "" - else -> "" - } - ), - actionLabel = context.getString(R.string.dismiss), - duration = SnackbarDuration.Short - ) - } - onStop(lock, home) - } + onLockIntervalChange = { days, hours, minutes -> + val totalMinutes = 24 * days * 60 + hours * 60 + minutes + onLockTimeChange(totalMinutes) }, + onStartTimeChange = onStartTimeChange, + onShowIntervalChange = { showInterval.value = it }, + onChangeStartTimeToggle = onChangeStartTimeToggle, + homeEnabled = wallpaperSettings.setHomeWallpaper, + lockEnabled = wallpaperSettings.setLockWallpaper, + showInterval = showInterval.value, scheduleSeparately = scheduleSettings.scheduleSeparately, - enableChanger = wallpaperSettings.enableChanger, + changeStartTime = scheduleSettings.changeStartTime, animate = themeSettings.animate ) - if (shouldShowSettings) { - WallpaperPreviewAndScale( - currentHomeWallpaper = wallpaperSettings.currentHomeWallpaper, - currentLockWallpaper = wallpaperSettings.currentLockWallpaper, - scaling = wallpaperSettings.wallpaperScaling, - onScalingChange = onScalingChange, - homeBlurPercentage = effectSettings.homeBlurPercentage, - lockBlurPercentage = effectSettings.lockBlurPercentage, - homeDarkenPercentage = effectSettings.homeDarkenPercentage, - lockDarkenPercentage = effectSettings.lockDarkenPercentage, - homeVignettePercentage = effectSettings.homeVignettePercentage, - lockVignettePercentage = effectSettings.lockVignettePercentage, - homeGrayscalePercentage = effectSettings.homeGrayscalePercentage, - lockGrayscalePercentage = effectSettings.lockGrayscalePercentage, - homeEnabled = wallpaperSettings.setHomeWallpaper, - lockEnabled = wallpaperSettings.setLockWallpaper, - darken = effectSettings.darken, - blur = effectSettings.blur, - vignette = effectSettings.vignette, - grayscale = effectSettings.grayscale - ) - CurrentAndNextChange(scheduleSettings.lastSetTime, scheduleSettings.nextSetTime) - TimeSliders( - homeInterval = scheduleSettings.homeInterval, - lockInterval = scheduleSettings.lockInterval, - startingTime = scheduleSettings.startTime, - onHomeIntervalChange = { days, hours, minutes -> - val totalMinutes = 24 * days * 60 + hours * 60 + minutes - onHomeTimeChange(totalMinutes) - }, - onLockIntervalChange = { days, hours, minutes -> - val totalMinutes = 24 * days * 60 + hours * 60 + minutes - onLockTimeChange(totalMinutes) - }, - onStartTimeChange = onStartTimeChange, - onShowIntervalChange = { showInterval.value = it }, - onChangeStartTimeToggle = onChangeStartTimeToggle, - homeEnabled = wallpaperSettings.setHomeWallpaper, - lockEnabled = wallpaperSettings.setLockWallpaper, - showInterval = showInterval.value, - scheduleSeparately = scheduleSettings.scheduleSeparately, - changeStartTime = scheduleSettings.changeStartTime, - animate = themeSettings.animate - ) - DarkenSwitchAndSlider( - homeDarkenPercentage = effectSettings.homeDarkenPercentage, - lockDarkenPercentage = effectSettings.lockDarkenPercentage, - onDarkCheck = onDarkCheck, - onDarkenChange = onDarkenPercentage, - darken = effectSettings.darken, - animate = themeSettings.animate, - bothEnabled = wallpaperSettings.setHomeWallpaper && wallpaperSettings.setLockWallpaper - ) - BlurSwitchAndSlider( - homeBlurPercentage = effectSettings.homeBlurPercentage, - lockBlurPercentage = effectSettings.lockBlurPercentage, - onBlurPercentageChange = onBlurPercentageChange, - onBlurChange = onBlurChange, - blur = effectSettings.blur, - animate = themeSettings.animate, - bothEnabled = wallpaperSettings.setHomeWallpaper && wallpaperSettings.setLockWallpaper - ) - VignetteSwitchAndSlider( - homeVignettePercentage = effectSettings.homeVignettePercentage, - lockVignettePercentage = effectSettings.lockVignettePercentage, - onVignettePercentageChange = onVignettePercentageChange, - onVignetteChange = onVignetteChange, - vignette = effectSettings.vignette, - animate = themeSettings.animate, - bothEnabled = wallpaperSettings.setHomeWallpaper && wallpaperSettings.setLockWallpaper - ) - GrayscaleSwitchAndSlider( - homeGrayscalePercentage = effectSettings.homeGrayscalePercentage, - lockGrayscalePercentage = effectSettings.lockGrayscalePercentage, - onGrayscalePercentageChange = onGrayscalePercentageChange, - onGrayscaleChange = onGrayscaleChange, - grayscale = effectSettings.grayscale, - animate = themeSettings.animate, - bothEnabled = wallpaperSettings.setHomeWallpaper && wallpaperSettings.setLockWallpaper - ) - } + DarkenSwitchAndSlider( + homeDarkenPercentage = effectSettings.homeDarkenPercentage, + lockDarkenPercentage = effectSettings.lockDarkenPercentage, + onDarkCheck = onDarkCheck, + onDarkenChange = onDarkenPercentage, + darken = effectSettings.darken, + animate = themeSettings.animate, + bothEnabled = wallpaperSettings.setHomeWallpaper && wallpaperSettings.setLockWallpaper + ) + BlurSwitchAndSlider( + homeBlurPercentage = effectSettings.homeBlurPercentage, + lockBlurPercentage = effectSettings.lockBlurPercentage, + onBlurPercentageChange = onBlurPercentageChange, + onBlurChange = onBlurChange, + blur = effectSettings.blur, + animate = themeSettings.animate, + bothEnabled = wallpaperSettings.setHomeWallpaper && wallpaperSettings.setLockWallpaper + ) + VignetteSwitchAndSlider( + homeVignettePercentage = effectSettings.homeVignettePercentage, + lockVignettePercentage = effectSettings.lockVignettePercentage, + onVignettePercentageChange = onVignettePercentageChange, + onVignetteChange = onVignetteChange, + vignette = effectSettings.vignette, + animate = themeSettings.animate, + bothEnabled = wallpaperSettings.setHomeWallpaper && wallpaperSettings.setLockWallpaper + ) + GrayscaleSwitchAndSlider( + homeGrayscalePercentage = effectSettings.homeGrayscalePercentage, + lockGrayscalePercentage = effectSettings.lockGrayscalePercentage, + onGrayscalePercentageChange = onGrayscalePercentageChange, + onGrayscaleChange = onGrayscaleChange, + grayscale = effectSettings.grayscale, + animate = themeSettings.animate, + bothEnabled = wallpaperSettings.setHomeWallpaper && wallpaperSettings.setLockWallpaper + ) } } + if (shouldShowScreen && openBottomSheet) { AlbumBottomSheet( albums = albums, homeSelectedAlbum = homeSelectedAlbum, lockSelectedAlbum = lockSelectedAlbum, - onSelect = { album -> - openBottomSheet = false - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - val alarmManager = ContextCompat.getSystemService(context, AlarmManager::class.java) - if (alarmManager?.canScheduleExactAlarms() == false) { - Intent().also { intent -> - intent.action = Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM - context.startActivity(intent) - } - } - else { - onSelectAlbum(album, lockEnabled.value, homeEnabled.value) - } - } - else { - onSelectAlbum(album, lockEnabled.value, homeEnabled.value) - } - }, + onSelect = handleAlbumSelection, onDismiss = { openBottomSheet = false }, animate = themeSettings.animate ) } - }, - ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperScreenViewModel.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperScreenViewModel.kt deleted file mode 100644 index 04a558bf..00000000 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperScreenViewModel.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.anthonyla.paperize.feature.wallpaper.presentation.wallpaper_screen - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.anthonyla.paperize.feature.wallpaper.domain.model.AlbumWithWallpaperAndFolder -import com.anthonyla.paperize.feature.wallpaper.domain.repository.AlbumRepository -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class WallpaperScreenViewModel @Inject constructor( - private val repository: AlbumRepository, -) : ViewModel() { - - private val _state = MutableStateFlow(WallpaperState()) - val state = combine( - loadSelectedAlbumFlow(), - _state - ) { selectedAlbum, currentState -> - currentState.copy( - selectedAlbum = selectedAlbum, - isDataLoaded = true - ) - }.stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(5000), - WallpaperState() - ) - - private fun loadSelectedAlbumFlow(): Flow?> = - repository.getSelectedAlbums() - - fun onEvent(event: WallpaperEvent) { - when (event) { - is WallpaperEvent.AddSelectedAlbum -> { - viewModelScope.launch { - event.deselectAlbumName?.let { - repository.updateAlbumSelection(it, false) - } - repository.updateAlbumSelection(event.album.album.initialAlbumName, true) - } - } - - is WallpaperEvent.RemoveSelectedAlbum -> { - viewModelScope.launch { - repository.updateAlbumSelection(event.deselectAlbumName, false) - } - } - - is WallpaperEvent.Reset -> { - viewModelScope.launch { - repository.deselectAllAlbums() - } - } - } - } -} diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperState.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperState.kt deleted file mode 100644 index 429a5889..00000000 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/WallpaperState.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.anthonyla.paperize.feature.wallpaper.presentation.wallpaper_screen - -import com.anthonyla.paperize.feature.wallpaper.domain.model.AlbumWithWallpaperAndFolder - -data class WallpaperState ( - val isDataLoaded: Boolean = false, - val selectedAlbum: List? = null -) \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/components/AlbumBottomSheet.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/components/AlbumBottomSheet.kt index a88f40de..7f4506be 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/components/AlbumBottomSheet.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/components/AlbumBottomSheet.kt @@ -35,6 +35,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import com.anthonyla.paperize.R +import com.anthonyla.paperize.core.decompress import com.anthonyla.paperize.core.isValidUri import com.anthonyla.paperize.feature.wallpaper.domain.model.AlbumWithWallpaperAndFolder import com.skydoves.landscapist.ImageOptions @@ -47,77 +48,84 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable fun AlbumBottomSheet( - onDismiss: () -> Unit, homeSelectedAlbum: AlbumWithWallpaperAndFolder?, lockSelectedAlbum: AlbumWithWallpaperAndFolder?, albums: List, + animate: Boolean, onSelect: (AlbumWithWallpaperAndFolder) -> Unit, - animate: Boolean + onDismiss: () -> Unit, ) { val modalBottomSheetState = rememberModalBottomSheetState() val scope = rememberCoroutineScope() + val context = LocalContext.current + + val navigationBarPadding = WindowInsets.navigationBars + .asPaddingValues() + .calculateBottomPadding() + + val sheetModifier = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + Modifier + .fillMaxSize() + .padding(bottom = navigationBarPadding) + } else { + Modifier.padding(bottom = navigationBarPadding) + } + ModalBottomSheet( - onDismissRequest = { onDismiss() }, + onDismissRequest = onDismiss, sheetState = modalBottomSheetState, dragHandle = { BottomSheetDefaults.DragHandle() }, containerColor = MaterialTheme.colorScheme.background, tonalElevation = 5.dp, - modifier = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { - Modifier - .fillMaxSize() - .padding( - bottom = WindowInsets.navigationBars - .asPaddingValues() - .calculateBottomPadding() - ) - } else { - Modifier.padding(bottom = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()) - } + modifier = sheetModifier ) { - albums.forEach { - if (it.folders.sumOf { folder -> folder.wallpapers.size } + it.wallpapers.size == 0) return@forEach + albums.forEach { album -> + val totalWallpapers = album.folders.sumOf { it.wallpapers.size } + album.wallpapers.size + if (totalWallpapers == 0) return@forEach + + val isSelected = (homeSelectedAlbum?.album?.initialAlbumName == album.album.initialAlbumName) || + (lockSelectedAlbum?.album?.displayedAlbumName == album.album.displayedAlbumName) + ListItem( - modifier = Modifier - .clickable { - scope.launch { - modalBottomSheetState.hide() - onSelect(it) - onDismiss() - } - }, + modifier = Modifier.clickable { + scope.launch { + modalBottomSheetState.hide() + } + onSelect(album) + onDismiss() + }, headlineContent = { Text( - text = it.album.displayedAlbumName, + text = album.album.displayedAlbumName, style = MaterialTheme.typography.titleMedium - ) }, + ) + }, supportingContent = { - val totalWallpapers = it.folders.sumOf { folder -> folder.wallpapers.size } + it.wallpapers.size Text( - text = LocalContext.current.resources.getQuantityString(R.plurals.wallpaper_count, totalWallpapers, totalWallpapers), + text = context.resources.getQuantityString( + R.plurals.wallpaper_count, + totalWallpapers, + totalWallpapers + ), style = MaterialTheme.typography.bodySmall, overflow = TextOverflow.Ellipsis ) }, leadingContent = { - Box ( - modifier = Modifier.size(60.dp) - ) { - val showCoverUri = isValidUri(LocalContext.current, it.album.coverUri) - if (showCoverUri) { + Box(modifier = Modifier.size(60.dp)) { + if (isValidUri(context, album.album.coverUri)) { GlideImage( - imageModel = { it.album.coverUri }, + imageModel = { album.album.coverUri?.decompress("content://com.android.externalstorage.documents/") }, imageOptions = ImageOptions( contentScale = ContentScale.Crop, alignment = Alignment.Center, - requestSize = IntSize(150, 150), + requestSize = IntSize(150, 150) ), loading = { if (animate) { Box(modifier = Modifier.matchParentSize()) { CircularProgressIndicator( - modifier = Modifier.align( - Alignment.Center - ) + modifier = Modifier.align(Alignment.Center) ) } } @@ -130,13 +138,15 @@ fun AlbumBottomSheet( } }, trailingContent = { - val isSelected = (homeSelectedAlbum?.album?.initialAlbumName == it.album.initialAlbumName) || - (lockSelectedAlbum?.album?.displayedAlbumName == it.album.displayedAlbumName) Icon( - contentDescription = if (isSelected) stringResource(R.string.currently_selected_album) else stringResource(R.string.unselected_album), - imageVector = if (isSelected) Icons.Filled.RadioButtonChecked else Icons.Filled.RadioButtonUnchecked, + imageVector = if (isSelected) Icons.Filled.RadioButtonChecked + else Icons.Filled.RadioButtonUnchecked, + contentDescription = stringResource( + if (isSelected) R.string.currently_selected_album + else R.string.unselected_album + ) ) - }, + } ) } } diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/components/CurrentSelectedAlbum.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/components/CurrentSelectedAlbum.kt index e3405324..963b1335 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/components/CurrentSelectedAlbum.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_screen/components/CurrentSelectedAlbum.kt @@ -1,14 +1,11 @@ package com.anthonyla.paperize.feature.wallpaper.presentation.wallpaper_screen.components -import android.content.Context import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -37,15 +34,13 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp -import androidx.core.net.toUri import com.anthonyla.paperize.R +import com.anthonyla.paperize.core.decompress +import com.anthonyla.paperize.core.isValidUri import com.anthonyla.paperize.feature.wallpaper.domain.model.AlbumWithWallpaperAndFolder import com.skydoves.landscapist.ImageOptions import com.skydoves.landscapist.glide.GlideImage -/** - * The inner card for the album bottom sheet to represent each album - */ @Composable fun CurrentSelectedAlbum( homeSelectedAlbum: AlbumWithWallpaperAndFolder?, @@ -53,179 +48,110 @@ fun CurrentSelectedAlbum( scheduleSeparately: Boolean, enableChanger: Boolean, animate: Boolean, - onOpenBottomSheet: (Boolean, Boolean) -> Unit, // lock, home - onStop: (Boolean, Boolean) -> Unit, + onOpenBottomSheet: (lock: Boolean, home: Boolean) -> Unit, + onDeselect: (lock: Boolean, home: Boolean) -> Unit, onToggleChanger: (Boolean) -> Unit ) { - fun isValidUri(context: Context, uriString: String?): Boolean { - val uri = uriString?.toUri() - return try { - uri?.let { - val inputStream = context.contentResolver.openInputStream(it) - inputStream?.close() - } - true - } catch (e: Exception) { false } + val context = LocalContext.current + val showHomeCoverUri = isValidUri(context, homeSelectedAlbum?.album?.coverUri) + val showLockCoverUri = isValidUri(context, lockSelectedAlbum?.album?.coverUri) + + val baseListItemModifier = { horizontal: Int -> + Modifier + .padding(PaddingValues(vertical = 8.dp, horizontal = horizontal.dp)) + .clip(RoundedCornerShape(16.dp)) } - val showHomeCoverUri = isValidUri(LocalContext.current, homeSelectedAlbum?.album?.coverUri) - val showLockCoverUri = isValidUri(LocalContext.current, lockSelectedAlbum?.album?.coverUri) + val albumImage = @Composable { album: AlbumWithWallpaperAndFolder?, showCoverUri: Boolean, size: Int -> + Box(modifier = Modifier.size(size.dp)) { + if (album != null && showCoverUri) { + GlideImage( + imageModel = { album.album.coverUri?.decompress("content://com.android.externalstorage.documents/") }, + imageOptions = ImageOptions( + contentScale = ContentScale.Crop, + alignment = Alignment.Center, + requestSize = IntSize(150, 150), + ), + loading = { + if (animate) { + Box(modifier = Modifier.matchParentSize()) { + CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) + } + } + }, + modifier = Modifier + .aspectRatio(1f) + .clip(RoundedCornerShape(16.dp)) + ) + } else { + Image( + imageVector = Icons.Default.RadioButtonUnchecked, + contentDescription = stringResource(R.string.no_icon), + modifier = Modifier.fillMaxSize() + ) + } + } + } if (scheduleSeparately) { Row { + // Lock screen album ListItem( - modifier = Modifier - .padding(PaddingValues(top = 8.dp, bottom = 8.dp, start = 16.dp, end = 8.dp)) + modifier = baseListItemModifier(16) .weight(1f) - .clip(RoundedCornerShape(16.dp)) .clickable { onOpenBottomSheet(true, false) }, headlineContent = { Row(modifier = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.SpaceBetween) { - Box (modifier = Modifier.weight(1f)) { + Box(modifier = Modifier.weight(1f)) { if (lockSelectedAlbum != null) { - IconButton( - onClick = { onStop(true, false) } - ) { - Icon( - imageVector = Icons.Default.Stop, - contentDescription = stringResource(R.string.stop_the_album) - ) + IconButton(onClick = { onDeselect(true, false) }) { + Icon(Icons.Default.Stop, stringResource(R.string.stop_the_album)) } } } - Spacer(modifier = Modifier.size(8.dp)) - Box (modifier = Modifier.weight(1f)) { - IconButton( - onClick = { onOpenBottomSheet(true, false) } - ) { - Icon( - contentDescription = stringResource(R.string.click_to_select_a_different_album), - imageVector = Icons.Filled.ArrowDropDown, - ) + Box(modifier = Modifier.weight(1f)) { + IconButton(onClick = { onOpenBottomSheet(true, false) }) { + Icon(Icons.Filled.ArrowDropDown, stringResource(R.string.click_to_select_a_different_album)) } } } }, + leadingContent = { albumImage(lockSelectedAlbum, showLockCoverUri, 48) }, supportingContent = {}, - leadingContent = { - Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { - Box(modifier = Modifier.size(48.dp)) { - if (lockSelectedAlbum != null) { - if (showLockCoverUri) { - GlideImage( - imageModel = { lockSelectedAlbum.album.coverUri }, - imageOptions = ImageOptions( - contentScale = ContentScale.Crop, - alignment = Alignment.Center, - requestSize = IntSize(150, 150), - ), - loading = { - if (animate) { - Box(modifier = Modifier.matchParentSize()) { - CircularProgressIndicator( - modifier = Modifier.align( - Alignment.Center - ) - ) - } - } - }, - modifier = Modifier - .aspectRatio(1f) - .clip(RoundedCornerShape(16.dp)) - ) - } - } else { - Image( - imageVector = Icons.Default.RadioButtonUnchecked, - contentDescription = stringResource(R.string.no_icon), - modifier = Modifier.fillMaxSize() - ) - } - } - } - }, trailingContent = {}, tonalElevation = 10.dp ) + + // Home screen album ListItem( - modifier = Modifier - .padding(PaddingValues(top = 8.dp, bottom = 8.dp, start = 8.dp, end = 16.dp)) + modifier = baseListItemModifier(16) .weight(1f) - .clip(RoundedCornerShape(16.dp)) .clickable { onOpenBottomSheet(false, true) }, headlineContent = { Row(modifier = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.SpaceBetween) { - Box (modifier = Modifier.weight(1f)) { + Box(modifier = Modifier.weight(1f)) { if (homeSelectedAlbum != null) { - IconButton(onClick = { onStop(false, true) }) { - Icon( - imageVector = Icons.Default.Stop, - contentDescription = stringResource(R.string.stop_the_album) - ) + IconButton(onClick = { onDeselect(false, true) }) { + Icon(Icons.Default.Stop, stringResource(R.string.stop_the_album)) } } } - Spacer(modifier = Modifier.size(8.dp)) - Box (modifier = Modifier.weight(1f)) { + Box(modifier = Modifier.weight(1f)) { IconButton(onClick = { onOpenBottomSheet(false, true) }) { - Icon( - contentDescription = stringResource(R.string.click_to_select_a_different_album), - imageVector = Icons.Filled.ArrowDropDown, - ) + Icon(Icons.Filled.ArrowDropDown, stringResource(R.string.click_to_select_a_different_album)) } } } }, + leadingContent = { albumImage(homeSelectedAlbum, showHomeCoverUri, 48) }, supportingContent = {}, - leadingContent = { - Box(modifier = Modifier.size(48.dp)) { - if (homeSelectedAlbum != null) { - if (showHomeCoverUri) { - GlideImage( - imageModel = { homeSelectedAlbum.album.coverUri }, - imageOptions = ImageOptions( - contentScale = ContentScale.Crop, - alignment = Alignment.Center, - requestSize = IntSize(150, 150), - ), - loading = { - if (animate) { - Box(modifier = Modifier.matchParentSize()) { - CircularProgressIndicator( - modifier = Modifier.align( - Alignment.Center - ) - ) - } - } - }, - modifier = Modifier - .aspectRatio(1f) - .clip(RoundedCornerShape(16.dp)) - ) - } - } else { - Image( - imageVector = Icons.Default.RadioButtonUnchecked, - contentDescription = stringResource(R.string.no_icon), - modifier = Modifier.fillMaxSize() - ) - } - } - }, - trailingContent = { - - }, + trailingContent = {}, tonalElevation = 10.dp ) } - } - else { + } else { ListItem( - modifier = Modifier - .padding(PaddingValues(vertical = 8.dp, horizontal = 16.dp)) - .clip(RoundedCornerShape(16.dp)) + modifier = baseListItemModifier(16) .clickable { onOpenBottomSheet(true, true) }, headlineContent = { Text( @@ -236,82 +162,37 @@ fun CurrentSelectedAlbum( ) }, supportingContent = { - if (homeSelectedAlbum != null) { + homeSelectedAlbum?.let { Text( - text = LocalContext.current.resources.getQuantityString( + text = context.resources.getQuantityString( R.plurals.wallpaper_count, - homeSelectedAlbum.wallpapers.size, - homeSelectedAlbum.wallpapers.size + it.folders.sumOf { folder -> folder.wallpapers.size } + it.wallpapers.size, + it.folders.sumOf { folder -> folder.wallpapers.size } + it.wallpapers.size ), style = MaterialTheme.typography.bodySmall, overflow = TextOverflow.Ellipsis ) } }, - leadingContent = { - Box(modifier = Modifier.size(60.dp)) { - if (homeSelectedAlbum != null) { - if (showHomeCoverUri) { - GlideImage( - imageModel = { homeSelectedAlbum.album.coverUri }, - imageOptions = ImageOptions( - contentScale = ContentScale.Crop, - alignment = Alignment.Center, - requestSize = IntSize(150, 150), - ), - loading = { - if (animate) { - Box(modifier = Modifier.matchParentSize()) { - CircularProgressIndicator( - modifier = Modifier.align( - Alignment.Center - ) - ) - } - } - }, - modifier = Modifier - .aspectRatio(1f) - .clip(RoundedCornerShape(16.dp)) - ) - } - } else { - Image( - imageVector = Icons.Default.RadioButtonUnchecked, - contentDescription = stringResource(R.string.no_icon), - modifier = Modifier.fillMaxSize() - ) - } - } - }, + leadingContent = { albumImage(homeSelectedAlbum, showHomeCoverUri, 60) }, trailingContent = { Row( horizontalArrangement = Arrangement.spacedBy((-8).dp), verticalAlignment = Alignment.CenterVertically ) { if (homeSelectedAlbum != null) { - IconButton(onClick = { onStop(true, true) }) { - Icon( - imageVector = Icons.Default.Stop, - contentDescription = stringResource(R.string.stop_the_album) - ) + IconButton(onClick = { onDeselect(true, true) }) { + Icon(Icons.Default.Stop, stringResource(R.string.stop_the_album)) } - } - IconButton(onClick = { onOpenBottomSheet(true, true) }) { - Icon( - contentDescription = stringResource(R.string.click_to_select_a_different_album), - imageVector = Icons.Filled.ArrowDropDown, - ) - } - if (homeSelectedAlbum != null) { Switch( checked = enableChanger, onCheckedChange = onToggleChanger, - modifier = Modifier - .rotate(270f) - .scale(0.75f) + modifier = Modifier.rotate(270f).scale(0.75f) ) } + IconButton(onClick = { onOpenBottomSheet(true, true) }) { + Icon(Icons.Filled.ArrowDropDown, stringResource(R.string.click_to_select_a_different_album)) + } } }, tonalElevation = 10.dp diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_view_screen/WallpaperViewScreen.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_view_screen/WallpaperViewScreen.kt index 9f206344..5e358848 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_view_screen/WallpaperViewScreen.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/wallpaper_view_screen/WallpaperViewScreen.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.net.toUri import com.anthonyla.paperize.R +import com.anthonyla.paperize.core.decompress import com.anthonyla.paperize.core.isValidUri import com.skydoves.landscapist.ImageOptions import com.skydoves.landscapist.glide.GlideImage @@ -90,7 +91,7 @@ fun WallpaperViewScreen( ) { if (showUri) { GlideImage( - imageModel = { wallpaperUri.toUri() }, + imageModel = { wallpaperUri.decompress("content://com.android.externalstorage.documents/").toUri() }, imageOptions = ImageOptions( contentScale = ContentScale.Fit, alignment = Alignment.Center diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index aae4ce33..94b0a86c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -173,4 +173,5 @@ Wallpapers Close Expand + %1$s selected \ No newline at end of file