From ec54bbb8c50452a20c8def90996b68d2d57f1c9e Mon Sep 17 00:00:00 2001 From: Anthonyy232 Date: Fri, 31 May 2024 01:29:58 -0700 Subject: [PATCH 1/8] Fixed navigation bug when going to home screen --- .../paperize/feature/wallpaper/presentation/PaperizeApp.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 d86a59e1..9db3e3d0 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 @@ -92,7 +92,11 @@ fun PaperizeApp( albumsViewModel.onEvent(AlbumsEvent.InitializeAlbum(albumWithWallpapers)) } else if (albumWithWallpapers.wallpapers.isEmpty() && albumWithWallpapers.folders.isEmpty() && albumWithWallpapers.album.initialized) { if (navController.currentDestination?.route == Home::class.simpleName) { - navController.popBackStack(inclusive = false) + try { + navController.popBackStack(inclusive = false) + } catch (e: Exception) { + navController.navigate(Home) + } } albumsViewModel.onEvent( AlbumsEvent.DeleteAlbumWithWallpapers( From 996208b03def1b38abeee173b28df2b38dad46f7 Mon Sep 17 00:00:00 2001 From: Anthonyy232 Date: Sun, 2 Jun 2024 02:08:27 -0700 Subject: [PATCH 2/8] Migrated wallpaper changer functionality to alarm manager-service approach to improve reliability for long intervals Fixed navigation bug when resetting from settings or going from notifications page to home page navigating twice --- app/build.gradle.kts | 40 +- app/src/main/AndroidManifest.xml | 19 +- .../paperize/core/SettingsConstants.kt | 2 + .../wallpaper/presentation/MainActivity.kt | 41 - .../wallpaper/presentation/PaperizeApp.kt | 206 ++--- .../settings_screen/SettingsViewModel.kt | 34 +- .../WallpaperAlarmItem.kt | 15 + .../WallpaperAlarmScheduler.kt | 27 + .../WallpaperBootAndTimeChangeReceiver.kt | 59 ++ .../WallpaperReceiver.kt | 56 ++ .../WallpaperScheduler.kt | 185 +++++ .../WallpaperBootReceiver.kt | 29 - .../wallpaper_service/WallpaperService1.kt | 709 ++++++++++++++++++ ...llpaperService.kt => WallpaperService2.kt} | 368 +++------ 14 files changed, 1271 insertions(+), 519 deletions(-) create mode 100644 app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperAlarmItem.kt create mode 100644 app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperAlarmScheduler.kt create mode 100644 app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperBootAndTimeChangeReceiver.kt create mode 100644 app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperReceiver.kt create mode 100644 app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperScheduler.kt delete mode 100644 app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperBootReceiver.kt create mode 100644 app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt rename app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/{WallpaperService.kt => WallpaperService2.kt} (70%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f7f97e88..08e880dc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -18,8 +18,8 @@ android { applicationId = "com.anthonyla.paperize" minSdk = 26 targetSdk = 34 - versionCode = 15 - versionName = "1.4.1" + versionCode = 16 + versionName = "1.5.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { @@ -67,40 +67,40 @@ androidComponents { dependencies { implementation("androidx.core:core-ktx:1.13.1") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.1") implementation("androidx.activity:activity-compose:1.9.0") implementation(platform("androidx.compose:compose-bom:2024.05.00")) - implementation("androidx.compose.ui:ui:1.7.0-beta01") - implementation("androidx.compose.ui:ui-graphics:1.7.0-beta01") - implementation("androidx.compose.ui:ui-tooling-preview:1.7.0-beta01") - implementation("androidx.compose.material3:material3:1.3.0-beta01") - implementation("androidx.navigation:navigation-compose:2.8.0-beta01") - implementation("androidx.compose.material:material:1.7.0-beta01") + implementation("androidx.compose.ui:ui:1.7.0-beta02") + implementation("androidx.compose.ui:ui-graphics:1.7.0-beta02") + implementation("androidx.compose.ui:ui-tooling-preview:1.7.0-beta02") + implementation("androidx.compose.material3:material3:1.3.0-beta02") + implementation("androidx.navigation:navigation-compose:2.8.0-beta02") + implementation("androidx.compose.material:material:1.7.0-beta02") implementation("androidx.datastore:datastore:1.1.1") implementation("androidx.datastore:datastore-preferences:1.1.1") - implementation("androidx.compose.material:material-icons-extended:1.7.0-beta01") + implementation("androidx.compose.material:material-icons-extended:1.7.0-beta02") implementation("com.google.accompanist:accompanist-adaptive:0.34.0") implementation("androidx.hilt:hilt-navigation-compose:1.2.0") - implementation("androidx.compose.animation:animation:1.7.0-beta01") + implementation("androidx.compose.animation:animation:1.7.0-beta02") implementation("androidx.core:core-splashscreen:1.2.0-alpha01") - implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.0") + implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.1") implementation("com.google.code.gson:gson:2.11.0") implementation("androidx.documentfile:documentfile:1.1.0-alpha01") implementation("net.engawapg.lib:zoomable:1.7.0-beta02") implementation("com.github.skydoves:landscapist-glide:2.3.3") implementation("androidx.work:work-runtime-ktx:2.10.0-alpha02") implementation("androidx.hilt:hilt-work:1.2.0") - implementation("com.airbnb.android:lottie-compose:6.4.0") + implementation("com.airbnb.android:lottie-compose:6.4.1") implementation("com.google.accompanist:accompanist-permissions:0.35.0-alpha") - implementation("com.mikepenz:aboutlibraries-core:11.2.0") - implementation("com.mikepenz:aboutlibraries-compose-m3:11.2.0") - implementation("androidx.compose.foundation:foundation:1.7.0-beta01") + implementation("com.mikepenz:aboutlibraries-core:11.2.1") + implementation("com.mikepenz:aboutlibraries-compose-m3:11.2.1") + implementation("androidx.compose.foundation:foundation:1.7.0-beta02") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") - androidTestImplementation("androidx.test.espresso:espresso-core:3.6.0-beta01") - androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.7.0-beta01") - debugImplementation("androidx.compose.ui:ui-tooling:1.7.0-beta01") - debugImplementation("androidx.compose.ui:ui-test-manifest:1.7.0-beta01") + androidTestImplementation("androidx.test.espresso:espresso-core:3.6.0-rc01") + androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.7.0-beta02") + debugImplementation("androidx.compose.ui:ui-tooling:1.7.0-beta02") + debugImplementation("androidx.compose.ui:ui-test-manifest:1.7.0-beta02") implementation("com.google.dagger:hilt-android:2.51.1") ksp("com.google.dagger:hilt-android-compiler:2.51.1") implementation("androidx.room:room-runtime:2.6.1") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 17d6e54c..de592afb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,17 +2,12 @@ + + - - - - - - - - - + + + + + + (null) } val scope = rememberCoroutineScope() + val scheduler = WallpaperScheduler(context) // React to albumState changes and change selectedAlbum's details to keep it from being stale LaunchedEffect(albumState.value.albumsWithWallpapers) { @@ -138,10 +140,7 @@ fun PaperizeApp( ) } ?: run { wallpaperScreenViewModel.onEvent(WallpaperEvent.Reset) - Intent(context, WallpaperService::class.java).also { - it.action = WallpaperService.Actions.STOP.toString() - context.startForegroundService(it) - } + scheduler.cancelWallpaperAlarm() } } } @@ -179,9 +178,6 @@ fun PaperizeApp( onAgree = { if (ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { settingsViewModel.onEvent(SettingsEvent.SetFirstLaunch) - navController.navigate(Home) { - popUpTo { inclusive = true } - } } else { navController.navigate(Notification) } } @@ -211,12 +207,7 @@ fun PaperizeApp( } ) { NotificationScreen( - onAgree = { - settingsViewModel.onEvent(SettingsEvent.SetFirstLaunch) - navController.navigate(Home) { - popUpTo { inclusive = true } - } - } + onAgree = { settingsViewModel.onEvent(SettingsEvent.SetFirstLaunch) } ) } // Navigate to the home screen to view all albums and wallpapers @@ -255,13 +246,12 @@ fun PaperizeApp( if (settingsState.value.enableChanger) { job?.cancel() job = scope.launch { - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.START.toString() - putExtra("timeInMinutes1", timeInMinutes) - putExtra("timeInMinutes2", settingsState.value.lockInterval) - putExtra("scheduleSeparately", settingsState.value.scheduleSeparately) - } - context.startForegroundService(intent) + val alarmItem = WallpaperAlarmItem( + timeInMinutes1 = timeInMinutes, + timeInMinutes2 = settingsState.value.lockInterval, + scheduleSeparately = settingsState.value.scheduleSeparately + ) + alarmItem.let{scheduler.scheduleWallpaperAlarm(it, null, true, true)} } } }, @@ -270,13 +260,12 @@ fun PaperizeApp( if (settingsState.value.enableChanger) { job?.cancel() job = scope.launch { - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.START.toString() - putExtra("timeInMinutes1", settingsState.value.homeInterval) - putExtra("timeInMinutes2", timeInMinutes) - putExtra("scheduleSeparately", settingsState.value.scheduleSeparately) - } - context.startForegroundService(intent) + val alarmItem = WallpaperAlarmItem( + timeInMinutes1 = settingsState.value.homeInterval, + timeInMinutes2 = timeInMinutes, + scheduleSeparately = settingsState.value.scheduleSeparately + ) + alarmItem.let{scheduler.scheduleWallpaperAlarm(it, null, true, true)} } } }, @@ -286,13 +275,12 @@ fun PaperizeApp( job?.cancel() job = scope.launch { delay(3000) - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.REQUEUE.toString() - putExtra("timeInMinutes1", timeInMinutes) - putExtra("timeInMinutes2", settingsState.value.lockInterval) - putExtra("scheduleSeparately", settingsState.value.scheduleSeparately) - } - context.startForegroundService(intent) + val alarmItem = WallpaperAlarmItem( + timeInMinutes1 = timeInMinutes, + timeInMinutes2 = settingsState.value.lockInterval, + scheduleSeparately = settingsState.value.scheduleSeparately + ) + alarmItem.let{scheduler.updateWallpaperAlarm(it)} } } @@ -303,13 +291,12 @@ fun PaperizeApp( job?.cancel() job = scope.launch { delay(3000) - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.REQUEUE.toString() - putExtra("timeInMinutes1", settingsState.value.homeInterval) - putExtra("timeInMinutes2", timeInMinutes) - putExtra("scheduleSeparately", settingsState.value.scheduleSeparately) - } - context.startForegroundService(intent) + val alarmItem = WallpaperAlarmItem( + timeInMinutes1 = settingsState.value.homeInterval, + timeInMinutes2 = timeInMinutes, + scheduleSeparately = settingsState.value.scheduleSeparately + ) + alarmItem.let{scheduler.updateWallpaperAlarm(it)} } } @@ -318,10 +305,7 @@ fun PaperizeApp( if (selectedState.value.selectedAlbum != null) { wallpaperScreenViewModel.onEvent(WallpaperEvent.Reset) settingsViewModel.onEvent(SettingsEvent.SetChangerToggle(false)) - Intent(context, WallpaperService::class.java).also { - it.action = WallpaperService.Actions.STOP.toString() - context.startForegroundService(it) - } + scheduler.cancelWallpaperAlarm() } }, animate = settingsState.value.animate, @@ -345,21 +329,15 @@ fun PaperizeApp( job?.cancel() job = scope.launch { delay(2000) - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.START.toString() - putExtra("timeInMinutes1", settingsState.value.homeInterval) - putExtra("timeInMinutes2", settingsState.value.lockInterval) - putExtra("scheduleSeparately", settingsState.value.scheduleSeparately) - } - context.startForegroundService(intent) - } - } - else { - Intent(context, WallpaperService::class.java).also { - it.action = WallpaperService.Actions.STOP.toString() - context.startForegroundService(it) + val alarmItem = WallpaperAlarmItem( + timeInMinutes1 = settingsState.value.homeInterval, + timeInMinutes2 = settingsState.value.lockInterval, + scheduleSeparately = settingsState.value.scheduleSeparately + ) + alarmItem.let{scheduler.scheduleWallpaperAlarm(it, null, true, true)} } } + else { scheduler.cancelWallpaperAlarm() } }, onSelectAlbum = {album -> settingsViewModel.onEvent(SettingsEvent.SetChangerToggle(true)) @@ -369,13 +347,12 @@ fun PaperizeApp( } job?.cancel() job = scope.launch { - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.START.toString() - putExtra("timeInMinutes1", settingsState.value.homeInterval) - putExtra("timeInMinutes2", settingsState.value.lockInterval) - putExtra("scheduleSeparately", settingsState.value.scheduleSeparately) - } - context.startForegroundService(intent) + val alarmItem = WallpaperAlarmItem( + timeInMinutes1 = settingsState.value.homeInterval, + timeInMinutes2 = settingsState.value.lockInterval, + scheduleSeparately = settingsState.value.scheduleSeparately + ) + alarmItem.let{scheduler.scheduleWallpaperAlarm(it, null, true, true)} } }, onDarkenPercentage = { @@ -384,10 +361,7 @@ fun PaperizeApp( job?.cancel() job = scope.launch { delay(2000) - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.UPDATE.toString() - } - context.startForegroundService(intent) + scheduler.updateWallpaper(settingsState.value.scheduleSeparately) } } }, @@ -397,10 +371,7 @@ fun PaperizeApp( job?.cancel() job = scope.launch { delay(2000) - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.UPDATE.toString() - } - context.startForegroundService(intent) + scheduler.updateWallpaper(settingsState.value.scheduleSeparately) } } }, @@ -412,10 +383,7 @@ fun PaperizeApp( job?.cancel() job = scope.launch { delay(2000) - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.UPDATE.toString() - } - context.startForegroundService(intent) + scheduler.updateWallpaper(settingsState.value.scheduleSeparately) } } } @@ -427,33 +395,26 @@ fun PaperizeApp( selectedState.value.selectedAlbum?.let { wallpaperScreenViewModel.onEvent(WallpaperEvent.Reset) } - Intent(context, WallpaperService::class.java).also { - it.action = WallpaperService.Actions.STOP.toString() - context.startForegroundService(it) - } + scheduler.cancelWallpaperAlarm() } else if (selectedState.value.selectedAlbum!= null && (enableHome && !settingsState.value.setLockWallpaper) || (!enableHome && settingsState.value.setLockWallpaper)) { settingsViewModel.onEvent(SettingsEvent.SetScheduleSeparately(false)) job?.cancel() job = scope.launch { delay(2000) - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.START.toString() - putExtra("timeInMinutes1", settingsState.value.homeInterval) - putExtra("timeInMinutes2", settingsState.value.lockInterval) - putExtra("scheduleSeparately", false) - } - context.startForegroundService(intent) + val alarmItem = WallpaperAlarmItem( + timeInMinutes1 = settingsState.value.homeInterval, + timeInMinutes2 = settingsState.value.lockInterval, + scheduleSeparately = false + ) + alarmItem.let{scheduler.scheduleWallpaperAlarm(it, null, true, true)} } } else if (selectedState.value.selectedAlbum != null && settingsState.value.enableChanger) { job?.cancel() job = scope.launch { delay(2000) - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.UPDATE.toString() - } - context.startForegroundService(intent) + scheduler.updateWallpaper(settingsState.value.scheduleSeparately) } } }, @@ -464,49 +425,40 @@ fun PaperizeApp( selectedState.value.selectedAlbum?.let { wallpaperScreenViewModel.onEvent(WallpaperEvent.Reset) } - Intent(context, WallpaperService::class.java).also { - it.action = WallpaperService.Actions.STOP.toString() - context.startForegroundService(it) - } + scheduler.cancelWallpaperAlarm() } else if (selectedState.value.selectedAlbum!= null && (enableLock && !settingsState.value.setHomeWallpaper) || (!enableLock && settingsState.value.setHomeWallpaper)) { settingsViewModel.onEvent(SettingsEvent.SetScheduleSeparately(false)) job?.cancel() job = scope.launch { delay(2000) - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.START.toString() - putExtra("timeInMinutes1", settingsState.value.homeInterval) - putExtra("timeInMinutes2", settingsState.value.lockInterval) - putExtra("scheduleSeparately", false) - } - context.startForegroundService(intent) - } + val alarmItem = WallpaperAlarmItem( + timeInMinutes1 = settingsState.value.homeInterval, + timeInMinutes2 = settingsState.value.lockInterval, + scheduleSeparately = false + ) + alarmItem.let{scheduler.scheduleWallpaperAlarm(it, null, true, true)}} } else if (selectedState.value.selectedAlbum != null && settingsState.value.enableChanger) { job?.cancel() job = scope.launch { delay(2000) - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.UPDATE.toString() - } - context.startForegroundService(intent) + scheduler.updateWallpaper(settingsState.value.scheduleSeparately) } } }, - onScheduleSeparatelyChange = { - settingsViewModel.onEvent(SettingsEvent.SetScheduleSeparately(it)) + onScheduleSeparatelyChange = { changeSeparately -> + settingsViewModel.onEvent(SettingsEvent.SetScheduleSeparately(changeSeparately)) if (selectedState.value.selectedAlbum!= null && settingsState.value.enableChanger) { job?.cancel() job = scope.launch { delay(2000) - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.START.toString() - putExtra("timeInMinutes1", settingsState.value.homeInterval) - putExtra("timeInMinutes2", settingsState.value.lockInterval) - putExtra("scheduleSeparately", it) - } - context.startForegroundService(intent) + val alarmItem = WallpaperAlarmItem( + timeInMinutes1 = settingsState.value.homeInterval, + timeInMinutes2 = settingsState.value.lockInterval, + scheduleSeparately = changeSeparately + ) + alarmItem.let{scheduler.scheduleWallpaperAlarm(it, null, true, true)} } } }, @@ -516,10 +468,7 @@ fun PaperizeApp( job?.cancel() job = scope.launch { delay(2000) - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.UPDATE.toString() - } - context.startForegroundService(intent) + scheduler.updateWallpaper(settingsState.value.scheduleSeparately) } } }, @@ -529,10 +478,7 @@ fun PaperizeApp( job?.cancel() job = scope.launch { delay(2000) - val intent = Intent(context, WallpaperService::class.java).apply { - action = WallpaperService.Actions.UPDATE.toString() - } - context.startForegroundService(intent) + scheduler.updateWallpaper(settingsState.value.scheduleSeparately) } } }, @@ -741,17 +687,11 @@ fun PaperizeApp( navController.navigate(Licenses) }, onResetClick = { - navController.navigate(Startup) { - popUpTo { inclusive = true } - } settingsViewModel.onEvent(SettingsEvent.Reset) wallpaperScreenViewModel.onEvent(WallpaperEvent.Reset) albumsViewModel.onEvent(AlbumsEvent.Reset) addAlbumViewModel.onEvent(AddAlbumEvent.Reset) - Intent(context, WallpaperService::class.java).also { - it.action = WallpaperService.Actions.STOP.toString() - context.startForegroundService(it) - } + scheduler.cancelWallpaperAlarm() val contentResolver = context.contentResolver val persistedUris = contentResolver.persistedUriPermissions for (permission in persistedUris) { diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/settings_screen/SettingsViewModel.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/settings_screen/SettingsViewModel.kt index 70f2e56d..69b307b9 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/settings_screen/SettingsViewModel.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/presentation/settings_screen/SettingsViewModel.kt @@ -183,10 +183,20 @@ class SettingsViewModel @Inject constructor ( } } + is SettingsEvent.SetFirstSet -> { + viewModelScope.launch(Dispatchers.IO) { + settingsDataStoreImpl.putBoolean(SettingsConstants.FIRST_SET, false) + _state.update { + it.copy( + firstSet = false + ) + } + } + } + is SettingsEvent.SetHomeWallpaperInterval -> { viewModelScope.launch(Dispatchers.IO) { settingsDataStoreImpl.putInt(SettingsConstants.HOME_WALLPAPER_CHANGE_INTERVAL, event.interval) - val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) val currentTime = LocalDateTime.now() val nextSetTime: String? @@ -196,10 +206,14 @@ class SettingsViewModel @Inject constructor ( val nextSetTime2 = currentTime.plusMinutes(_state.value.lockInterval.toLong()) nextSetTime = (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2)!!.format(formatter) settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, nextSetTime) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_1, nextSetTime1.toString()) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_2, nextSetTime2.toString()) } else { nextSetTime = currentTime.plusMinutes(event.interval.toLong()).format(formatter) settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, nextSetTime) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_1, currentTime.plusMinutes(event.interval.toLong()).toString()) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_2, currentTime.plusMinutes(event.interval.toLong()).toString()) } _state.update { it.copy( @@ -211,21 +225,9 @@ class SettingsViewModel @Inject constructor ( } } - is SettingsEvent.SetFirstSet -> { - viewModelScope.launch(Dispatchers.IO) { - settingsDataStoreImpl.putBoolean(SettingsConstants.FIRST_SET, false) - _state.update { - it.copy( - firstSet = false - ) - } - } - } - is SettingsEvent.SetLockWallpaperInterval -> { viewModelScope.launch(Dispatchers.IO) { settingsDataStoreImpl.putInt(SettingsConstants.LOCK_WALLPAPER_CHANGE_INTERVAL, event.interval) - val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) val currentTime = LocalDateTime.now() val nextSetTime: String? @@ -235,10 +237,14 @@ class SettingsViewModel @Inject constructor ( val nextSetTime2 = currentTime.plusMinutes(event.interval.toLong()) nextSetTime = (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2)!!.format(formatter) settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, nextSetTime) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_1, nextSetTime1.toString()) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_2, nextSetTime2.toString()) } else { nextSetTime = currentTime.plusMinutes(event.interval.toLong()).format(formatter) settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, nextSetTime) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_1, currentTime.plusMinutes(event.interval.toLong()).toString()) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_2, currentTime.plusMinutes(event.interval.toLong()).toString()) } _state.update { it.copy( @@ -380,6 +386,8 @@ class SettingsViewModel @Inject constructor ( settingsDataStoreImpl.deleteBoolean(SettingsConstants.BLUR) settingsDataStoreImpl.deleteInt(SettingsConstants.BLUR_PERCENTAGE) settingsDataStoreImpl.deleteBoolean(SettingsConstants.FIRST_SET) + settingsDataStoreImpl.deleteString(SettingsConstants.NEXT_SET_TIME_1) + settingsDataStoreImpl.deleteString(SettingsConstants.NEXT_SET_TIME_2) _state.update { it.copy( diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperAlarmItem.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperAlarmItem.kt new file mode 100644 index 00000000..15e6da43 --- /dev/null +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperAlarmItem.kt @@ -0,0 +1,15 @@ +package com.anthonyla.paperize.feature.wallpaper.wallpaper_alarmmanager + +import com.anthonyla.paperize.core.SettingsConstants + +/** + * Data class for storing wallpaper alarm item + * @param timeInMinutes1: Int - time in minutes for home wallpaper or both if scheduled together + * @param timeInMinutes2: Int - time in minutes for lock wallpaper + * @param scheduleSeparately: Boolean - schedule wallpapers separately + */ +data class WallpaperAlarmItem( + val timeInMinutes1: Int = SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT, + val timeInMinutes2: Int = SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT, + val scheduleSeparately: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperAlarmScheduler.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperAlarmScheduler.kt new file mode 100644 index 00000000..019b39d2 --- /dev/null +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperAlarmScheduler.kt @@ -0,0 +1,27 @@ +package com.anthonyla.paperize.feature.wallpaper.wallpaper_alarmmanager + +interface WallpaperAlarmScheduler { + /** + * Schedule a wallpaper alarm to change the wallpaper + * @param wallpaperAlarmItem the wallpaper alarm item to schedule + * @param origin where the request came from (null if not from a specific origin, 0 if home screen, 1 if lock screen, 2 if both) + * @param changeImmediate whether to change the wallpaper immediately or just schedule the alarm + * @param cancelImmediate whether to cancel all alarms before scheduling the new one + */ + fun scheduleWallpaperAlarm(wallpaperAlarmItem: WallpaperAlarmItem, origin: Int? = null, changeImmediate: Boolean = false, cancelImmediate: Boolean = false) + + /** + * Update the wallpaper alarm with new times without changing the wallpaper + */ + fun updateWallpaperAlarm(wallpaperAlarmItem: WallpaperAlarmItem) + + /** + * Update the wallpaper without changing the alarm + */ + fun updateWallpaper(scheduleSeparately: Boolean) + + /** + * Cancel all wallpaper alarms + */ + fun cancelWallpaperAlarm() +} \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperBootAndTimeChangeReceiver.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperBootAndTimeChangeReceiver.kt new file mode 100644 index 00000000..0e971a0f --- /dev/null +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperBootAndTimeChangeReceiver.kt @@ -0,0 +1,59 @@ +package com.anthonyla.paperize.feature.wallpaper.wallpaper_alarmmanager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import com.anthonyla.paperize.core.SettingsConstants +import com.anthonyla.paperize.data.settings.SettingsDataStore +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +/** + * Receiver for boot and time change events to restart alarm manager + */ +@AndroidEntryPoint +class WallpaperBootAndTimeChangeReceiver : BroadcastReceiver() { + @Inject lateinit var settingsDataStoreImpl: SettingsDataStore + override fun onReceive(context: Context, intent: Intent) = goAsync { + try { + if (Intent.ACTION_BOOT_COMPLETED == intent.action || Intent.ACTION_TIME_CHANGED == intent.action || Intent.ACTION_TIMEZONE_CHANGED == intent.action) { + val scheduler = WallpaperScheduler(context) + val toggleChanger = settingsDataStoreImpl.getBoolean(SettingsConstants.ENABLE_CHANGER) ?: false + if (toggleChanger) { + val timeInMinutes1 = settingsDataStoreImpl.getInt(SettingsConstants.HOME_WALLPAPER_CHANGE_INTERVAL) ?: SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT + val timeInMinutes2 = settingsDataStoreImpl.getInt(SettingsConstants.LOCK_WALLPAPER_CHANGE_INTERVAL) ?: SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT + val scheduleSeparately = settingsDataStoreImpl.getBoolean(SettingsConstants.SCHEDULE_SEPARATELY) ?: false + val alarmItem = WallpaperAlarmItem( + timeInMinutes1 = timeInMinutes1, + timeInMinutes2 = timeInMinutes2, + scheduleSeparately = scheduleSeparately + ) + alarmItem.let{scheduler.scheduleWallpaperAlarm(it, null, false, true)} + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + /* https://stackoverflow.com/questions/74111692/run-coroutine-functions-on-broadcast-receiver */ + private fun BroadcastReceiver.goAsync( + context: CoroutineContext = EmptyCoroutineContext, + block: suspend CoroutineScope.() -> Unit + ) { + val pendingResult = goAsync() + @OptIn(DelicateCoroutinesApi::class) + GlobalScope.launch(context) { + try { + block() + } finally { + pendingResult.finish() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperReceiver.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperReceiver.kt new file mode 100644 index 00000000..787f3a25 --- /dev/null +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperReceiver.kt @@ -0,0 +1,56 @@ +package com.anthonyla.paperize.feature.wallpaper.wallpaper_alarmmanager + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.util.Log +import com.anthonyla.paperize.core.SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT +import com.anthonyla.paperize.feature.wallpaper.wallpaper_service.WallpaperService1 +import com.anthonyla.paperize.feature.wallpaper.wallpaper_service.WallpaperService2 +import dagger.hilt.android.AndroidEntryPoint + +/** + * Receiver for the alarm manager to change wallpaper + */ +@AndroidEntryPoint +class WallpaperReceiver: BroadcastReceiver() { + enum class Type { HOME, LOCK, BOTH } + + override fun onReceive(context: Context?, intent: Intent?) { + if (context != null) { + val timeInMinutes1 = intent?.getIntExtra("timeInMinutes1", WALLPAPER_CHANGE_INTERVAL_DEFAULT) ?: WALLPAPER_CHANGE_INTERVAL_DEFAULT + val timeInMinutes2 = intent?.getIntExtra("timeInMinutes2", WALLPAPER_CHANGE_INTERVAL_DEFAULT) ?: WALLPAPER_CHANGE_INTERVAL_DEFAULT + val scheduleSeparately = intent?.getBooleanExtra("scheduleSeparately", false) ?: false + val type = intent?.getIntExtra("type", Type.BOTH.ordinal) ?: Type.BOTH.ordinal + Log.d("PaperizeWallpaperChanger", "onReceive: timeInMinutes1=$timeInMinutes1, timeInMinutes2=$timeInMinutes2, scheduleSeparately=$scheduleSeparately, type=$type") + + val serviceIntent = Intent().apply { + putExtra("timeInMinutes1", timeInMinutes1) + putExtra("timeInMinutes2", timeInMinutes2) + putExtra("scheduleSeparately", scheduleSeparately) + putExtra("type", type) + } + if (type == WallpaperScheduler.Type.BOTH.ordinal || type == WallpaperScheduler.Type.HOME.ordinal) { + serviceIntent.setClass(context, WallpaperService1::class.java).apply { + action = WallpaperService1.Actions.START.toString() + } + } else { + serviceIntent.setClass(context, WallpaperService2::class.java).apply { + action = WallpaperService2.Actions.START.toString() + } + } + context.startService(serviceIntent) + + // Schedule next alarm for next wallpaper change + val origin = intent?.getIntExtra("origin", -1)?.takeIf { it != -1 } + WallpaperScheduler(context).scheduleWallpaperAlarm( + WallpaperAlarmItem( + timeInMinutes1 = timeInMinutes1, + timeInMinutes2 = timeInMinutes2, + scheduleSeparately = scheduleSeparately + ), + origin + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperScheduler.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperScheduler.kt new file mode 100644 index 00000000..3a4724be --- /dev/null +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperScheduler.kt @@ -0,0 +1,185 @@ +package com.anthonyla.paperize.feature.wallpaper.wallpaper_alarmmanager + +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import com.anthonyla.paperize.feature.wallpaper.wallpaper_service.WallpaperService1 +import com.anthonyla.paperize.feature.wallpaper.wallpaper_service.WallpaperService2 +import java.time.LocalDateTime +import java.time.ZoneId + +/** + * This class is responsible for scheduling wallpaper alarms. + * It uses the AlarmManager to schedule alarms for the WallpaperReceiver. + * It also uses the WallpaperService to change the wallpaper due to receiver limitations. + */ +class WallpaperScheduler ( + private val context: Context, +): WallpaperAlarmScheduler { + private val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + enum class Type { HOME, LOCK, BOTH } + + override fun scheduleWallpaperAlarm(wallpaperAlarmItem: WallpaperAlarmItem, origin: Int?, changeImmediate: Boolean, cancelImmediate: Boolean) { + if (cancelImmediate) cancelWallpaperAlarm() + else { + when(origin) { + Type.HOME.ordinal -> cancelAlarm(Type.HOME) + Type.LOCK.ordinal -> cancelAlarm(Type.LOCK) + null -> { + cancelAlarm(Type.HOME) + cancelAlarm(Type.LOCK) + cancelAlarm(Type.BOTH) + } + } + } + if (wallpaperAlarmItem.scheduleSeparately) { + when (origin) { + Type.HOME.ordinal -> { + if (changeImmediate) changeWallpaperImmediate(wallpaperAlarmItem, Type.HOME) + scheduleWallpaper(wallpaperAlarmItem, Type.HOME, origin) + } + Type.LOCK.ordinal -> { + if (changeImmediate) changeWallpaperImmediate(wallpaperAlarmItem, Type.LOCK) + scheduleWallpaper(wallpaperAlarmItem, Type.LOCK, origin) + } + null -> { + if (changeImmediate) { + changeWallpaperImmediate(wallpaperAlarmItem, Type.HOME) + changeWallpaperImmediate(wallpaperAlarmItem, Type.LOCK) + } + scheduleWallpaper(wallpaperAlarmItem, Type.HOME, Type.HOME.ordinal) + scheduleWallpaper(wallpaperAlarmItem, Type.LOCK, Type.LOCK.ordinal) + } + } + } + else { + if (changeImmediate) changeWallpaperImmediate(wallpaperAlarmItem, Type.BOTH) + scheduleWallpaper(wallpaperAlarmItem, Type.BOTH) + } + } + + override fun updateWallpaperAlarm(wallpaperAlarmItem: WallpaperAlarmItem) { + cancelWallpaperAlarm() + if (wallpaperAlarmItem.scheduleSeparately) { + scheduleWallpaper(wallpaperAlarmItem, Type.HOME, null, true) + scheduleWallpaper(wallpaperAlarmItem, Type.LOCK, null, true) + } + else { + scheduleWallpaper(wallpaperAlarmItem, Type.BOTH, null, true) + } + } + + override fun updateWallpaper(scheduleSeparately: Boolean) { + if (scheduleSeparately) { + updateWallpaper(Type.HOME) + updateWallpaper(Type.LOCK) + } + else { + updateWallpaper(Type.BOTH) + } + } + + override fun cancelWallpaperAlarm() { + cancelAlarm(Type.HOME) + cancelAlarm(Type.LOCK) + cancelAlarm(Type.BOTH) + } + + /** + * Schedules the wallpaper alarm based on type and time + */ + private fun scheduleWallpaper(wallpaperAlarmItem: WallpaperAlarmItem, type: Type, origin: Int? = null, update: Boolean = false) { + val nextTime = when (type) { + Type.HOME, Type.BOTH -> LocalDateTime.now().plusMinutes(wallpaperAlarmItem.timeInMinutes1.toLong()) + Type.LOCK -> LocalDateTime.now().plusMinutes(wallpaperAlarmItem.timeInMinutes2.toLong()) + } + val intent = Intent(context, WallpaperReceiver::class.java).apply { + putExtra("timeInMinutes1", wallpaperAlarmItem.timeInMinutes1) + putExtra("timeInMinutes2", wallpaperAlarmItem.timeInMinutes2) + putExtra("scheduleSeparately", wallpaperAlarmItem.scheduleSeparately) + putExtra("type", type.ordinal) + putExtra("origin", origin) + } + alarmManager.setExactAndAllowWhileIdle( + AlarmManager.RTC_WAKEUP, + nextTime.atZone(ZoneId.systemDefault()).toEpochSecond() * 1000, + PendingIntent.getBroadcast( + context, + type.ordinal, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE + ) + ) + if (update) { + val serviceIntent = Intent().apply { + putExtra("timeInMinutes1", wallpaperAlarmItem.timeInMinutes1) + putExtra("timeInMinutes2", wallpaperAlarmItem.timeInMinutes2) + putExtra("scheduleSeparately", wallpaperAlarmItem.scheduleSeparately) + putExtra("type", type.ordinal) + } + if (type == Type.BOTH || type == Type.HOME) { + serviceIntent.setClass(context, WallpaperService1::class.java).apply { + action = WallpaperService1.Actions.REQUEUE.toString() + } + } else { + serviceIntent.setClass(context, WallpaperService2::class.java).apply { + action = WallpaperService2.Actions.REQUEUE.toString() + } + } + context.startService(serviceIntent) + } + } + + /** + * Use the service to change the wallpaper + */ + private fun changeWallpaperImmediate(wallpaperAlarmItem: WallpaperAlarmItem, type: Type) { + val serviceIntent = Intent().apply { + putExtra("timeInMinutes1", wallpaperAlarmItem.timeInMinutes1) + putExtra("timeInMinutes2", wallpaperAlarmItem.timeInMinutes2) + putExtra("scheduleSeparately", wallpaperAlarmItem.scheduleSeparately) + putExtra("type", type.ordinal) + } + if (type == Type.BOTH || type == Type.HOME) { + serviceIntent.setClass(context, WallpaperService1::class.java).apply { + action = WallpaperService1.Actions.START.toString() + } + } else { + serviceIntent.setClass(context, WallpaperService2::class.java).apply { + action = WallpaperService2.Actions.START.toString() + } + } + context.startService(serviceIntent) + } + + /** + * Updates the wallpaper without changing the alarm + */ + private fun updateWallpaper(type: Type) { + val serviceIntent = Intent() + if (type == Type.BOTH || type == Type.HOME) { + serviceIntent.setClass(context, WallpaperService1::class.java).apply { + action = WallpaperService1.Actions.UPDATE.toString() + } + } else { + serviceIntent.setClass(context, WallpaperService2::class.java).apply { + action = WallpaperService2.Actions.UPDATE.toString() + } + } + context.startService(serviceIntent) + } + + /** + * Cancels the alarm based on type + */ + private fun cancelAlarm(type: Type) { + val pendingIntent = PendingIntent.getBroadcast( + context, + type.ordinal, + Intent(context, WallpaperReceiver::class.java), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE + ) + alarmManager.cancel(pendingIntent) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperBootReceiver.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperBootReceiver.kt deleted file mode 100644 index 50ead8ea..00000000 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperBootReceiver.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.anthonyla.paperize.feature.wallpaper.wallpaper_service -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import com.anthonyla.paperize.core.SettingsConstants -import com.anthonyla.paperize.data.settings.SettingsDataStore -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.runBlocking -import javax.inject.Inject - -@AndroidEntryPoint -class WallpaperBootReceiver : BroadcastReceiver() { - @Inject lateinit var settingsDataStoreImpl: SettingsDataStore - override fun onReceive(context: Context, intent: Intent) { - if (Intent.ACTION_BOOT_COMPLETED == intent.action) { - val serviceIntent = Intent(context, WallpaperService::class.java) - serviceIntent.action = WallpaperService.Actions.START.toString() - runBlocking { - val timeInMinutes1 = settingsDataStoreImpl.getInt(SettingsConstants.HOME_WALLPAPER_CHANGE_INTERVAL) ?: SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT - val timeInMinutes2 = settingsDataStoreImpl.getInt(SettingsConstants.LOCK_WALLPAPER_CHANGE_INTERVAL) ?: SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT - val scheduleSeparately = settingsDataStoreImpl.getBoolean(SettingsConstants.SCHEDULE_SEPARATELY) ?: false - serviceIntent.putExtra("timeInMinutes1", timeInMinutes1) - serviceIntent.putExtra("timeInMinutes2", timeInMinutes2) - serviceIntent.putExtra("scheduleSeparately", scheduleSeparately) - } - context.startForegroundService(serviceIntent) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt new file mode 100644 index 00000000..fb64eecc --- /dev/null +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt @@ -0,0 +1,709 @@ +package com.anthonyla.paperize.feature.wallpaper.wallpaper_service + +import android.app.Notification +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.Service +import android.app.WallpaperManager +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.ImageDecoder +import android.net.Uri +import android.os.Build +import android.os.Handler +import android.os.HandlerThread +import android.os.IBinder +import android.util.DisplayMetrics +import android.util.Log +import androidx.core.app.NotificationCompat +import androidx.core.net.toUri +import androidx.documentfile.provider.DocumentFile +import com.anthonyla.paperize.R +import com.anthonyla.paperize.core.ScalingConstants +import com.anthonyla.paperize.core.SettingsConstants +import com.anthonyla.paperize.core.blurBitmap +import com.anthonyla.paperize.core.calculateInSampleSize +import com.anthonyla.paperize.core.darkenBitmap +import com.anthonyla.paperize.core.fillBitmap +import com.anthonyla.paperize.core.fitBitmap +import com.anthonyla.paperize.core.getImageDimensions +import com.anthonyla.paperize.core.getWallpaperFromFolder +import com.anthonyla.paperize.core.stretchBitmap +import com.anthonyla.paperize.data.settings.SettingsDataStore +import com.anthonyla.paperize.feature.wallpaper.domain.model.SelectedAlbum +import com.anthonyla.paperize.feature.wallpaper.domain.model.Wallpaper +import com.anthonyla.paperize.feature.wallpaper.domain.repository.AlbumRepository +import com.anthonyla.paperize.feature.wallpaper.domain.repository.SelectedAlbumRepository +import com.anthonyla.paperize.feature.wallpaper.presentation.MainActivity +import com.lazygeniouz.dfc.file.DocumentFileCompat +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import java.io.IOException +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle +import javax.inject.Inject +import kotlin.math.min + +/** + * Main service for changing the wallpaper if wallpapers are scheduled together + */ +@AndroidEntryPoint +class WallpaperService1: Service() { + private enum class Type { HOME, LOCK, BOTH } + private val handleThread = HandlerThread("MyThread1") + private lateinit var workerHandler: Handler + @Inject lateinit var selectedRepository: SelectedAlbumRepository + @Inject lateinit var albumRepository: AlbumRepository + @Inject lateinit var settingsDataStoreImpl: SettingsDataStore + private var scheduleSeparately: Boolean = false + private var timeInMinutes1: Int = SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT + private var timeInMinutes2: Int = SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT + private var nextSetTime1: LocalDateTime? = null + private var nextSetTime2: LocalDateTime? = null + + enum class Actions { + START, + REQUEUE, + UPDATE + } + + override fun onBind(p0: Intent?): IBinder? { + return null + } + + override fun onCreate() { + super.onCreate() + handleThread.start() + workerHandler = Handler(handleThread.looper) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + if (intent != null) { + when (intent.action) { + Actions.START.toString() -> { + timeInMinutes1 = intent.getIntExtra("timeInMinutes1", SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT) + timeInMinutes2 = intent.getIntExtra("timeInMinutes2", SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT) + scheduleSeparately = intent.getBooleanExtra("scheduleSeparately", false) + val setHomeOrLock = if (scheduleSeparately) { + when (intent.getIntExtra("type", Type.BOTH.ordinal)) { + Type.HOME.ordinal -> true + Type.LOCK.ordinal -> false + else -> null + } + } else { null } + workerTaskStart(setHomeOrLock) + } + Actions.REQUEUE.toString() -> { + timeInMinutes1 = intent.getIntExtra("timeInMinutes1", SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT) + timeInMinutes2 = intent.getIntExtra("timeInMinutes2", SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT) + scheduleSeparately = intent.getBooleanExtra("scheduleSeparately", false) + workerTaskRequeue() + } + Actions.UPDATE.toString() -> { + workerTaskUpdate() + } + } + } + return START_NOT_STICKY + } + + override fun onDestroy() { + super.onDestroy() + workerHandler.removeCallbacksAndMessages(null) + handleThread.quitSafely() + } + + private fun workerTaskStart(setHomeOrLock: Boolean? = null) { + workerHandler.post { + CoroutineScope(Dispatchers.IO).launch { + changeWallpaper(this@WallpaperService1, setHomeOrLock) + } + stopSelf() + } + } + + private fun workerTaskRequeue() { + workerHandler.post { + CoroutineScope(Dispatchers.IO).launch { + nextSetTime1 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_1)) + nextSetTime2 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_2)) + val notification = createNotification() + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notification?.let { notificationManager.notify(1, it) } + val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) + val currentTime = LocalDateTime.now() + settingsDataStoreImpl.putString(SettingsConstants.LAST_SET_TIME, currentTime.format(formatter)) + if (scheduleSeparately) { + nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) + nextSetTime2 = currentTime.plusMinutes(timeInMinutes2.toLong()) + val earliestTime = (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2)!!.format(formatter) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, earliestTime) + } + else { + nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) + nextSetTime2 = nextSetTime1 + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, currentTime.plusMinutes(timeInMinutes1.toLong()).format(formatter)) + } + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_1, nextSetTime1.toString()) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_2, nextSetTime2.toString()) + } + stopSelf() + } + } + + private fun workerTaskUpdate() { + workerHandler.post { + CoroutineScope(Dispatchers.IO).launch { + updateCurrentWallpaper(this@WallpaperService1) + } + stopSelf() + } + } + + /** + * Creates a notification for the wallpaper service + */ + private fun createNotification(): Notification? { + val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) + val earliestTime = when { + nextSetTime1 != null && nextSetTime2 != null -> (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2)?.format(formatter) + nextSetTime1 != null -> nextSetTime1!!.format(formatter) + nextSetTime2 != null -> nextSetTime2!!.format(formatter) + else -> null + } + if (earliestTime != null) { + val intent = Intent(this, MainActivity::class.java) + val pendingIntent = PendingIntent.getActivity(this, 3, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + return NotificationCompat.Builder(this, "wallpaper_service_channel") + .setContentTitle(getString(R.string.app_name)) + .setContentText(getString(R.string.next_wallpaper_change, earliestTime)) + .setSmallIcon(R.drawable.notification_icon) + .setContentIntent(pendingIntent) + .build() + } + return null + } + + /** + * Changes the wallpaper to the next wallpaper in the queue of the selected album + * If none left, reshuffle the wallpapers and pick the first one + */ + private suspend fun changeWallpaper(context: Context, setHomeOrLock: Boolean? = null) { + try { + val selectedAlbum = selectedRepository.getSelectedAlbum().first().firstOrNull() + if (selectedAlbum == null) { + onDestroy() + return + } + else { + val toggled = settingsDataStoreImpl.getBoolean(SettingsConstants.ENABLE_CHANGER) ?: false + val setHome = settingsDataStoreImpl.getBoolean(SettingsConstants.HOME_WALLPAPER) ?: false + val setLock = settingsDataStoreImpl.getBoolean(SettingsConstants.LOCK_WALLPAPER) ?: false + nextSetTime1 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_1)) + nextSetTime2 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_2)) + val notification = createNotification() + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notification?.let { notificationManager.notify(1, it) } + + if (!toggled || (!setHome && !setLock)) { + onDestroy() + return + } + val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) + val currentTime = LocalDateTime.now() + settingsDataStoreImpl.putString(SettingsConstants.LAST_SET_TIME, currentTime.format(formatter)) + + if (setHomeOrLock == null) { + nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) + nextSetTime2 = nextSetTime1 + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, currentTime.plusMinutes(timeInMinutes1.toLong()).format(formatter)) + } + else { + if (setHomeOrLock) { nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) } + else { nextSetTime2 = currentTime.plusMinutes(timeInMinutes2.toLong()) } + if (nextSetTime1 == null && nextSetTime2 != null) { + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, nextSetTime2!!.format(formatter)) + } + else if (nextSetTime1 != null && nextSetTime2 == null) { + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, nextSetTime1!!.format(formatter)) + } + else { + val earliestTime = if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2 + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, earliestTime!!.format(formatter)) + } + } + + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_1, nextSetTime1.toString()) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_2, nextSetTime2.toString()) + + val scaling = settingsDataStoreImpl.getString(SettingsConstants.WALLPAPER_SCALING)?.let { ScalingConstants.valueOf(it) } ?: ScalingConstants.FILL + val darken = settingsDataStoreImpl.getBoolean(SettingsConstants.DARKEN) ?: false + val darkenPercentage = settingsDataStoreImpl.getInt(SettingsConstants.DARKEN_PERCENTAGE) ?: 100 + val blur = settingsDataStoreImpl.getBoolean(SettingsConstants.BLUR) ?: false + val blurPercentage = settingsDataStoreImpl.getInt(SettingsConstants.BLUR_PERCENTAGE) ?: 0 + + selectedAlbum.let { it -> + if (setHomeOrLock != null) { + var wallpaper = if (setHomeOrLock) it.album.homeWallpapersInQueue.firstOrNull() else it.album.lockWallpapersInQueue.firstOrNull() + if (wallpaper != null) { + if (!setWallpaper( + context = context, + wallpaper = wallpaper.toUri(), + darken = darken, + darkenPercent = darkenPercentage, + scaling = scaling, + setHome = setHome, + setLock = setLock, + setLockOrHome = setHomeOrLock, + blur = blur, + blurPercent = blurPercentage + )) { + selectedAlbum.wallpapers.firstOrNull { it.wallpaperUri == wallpaper } + ?.let { it1 -> + selectedRepository.deleteWallpaper(it1) + selectedRepository.upsertSelectedAlbum( + it.copy( + album = it.album.copy( + homeWallpapersInQueue = if (setHomeOrLock) it.album.homeWallpapersInQueue.drop(1) else it.album.homeWallpapersInQueue, + lockWallpapersInQueue = if (!setHomeOrLock) it.album.lockWallpapersInQueue.drop(1) else it.album.lockWallpapersInQueue, + currentHomeWallpaper = if (setHomeOrLock) wallpaper else it.album.currentHomeWallpaper, + currentLockWallpaper = if (!setHomeOrLock) wallpaper else it.album.currentLockWallpaper + ), + wallpapers = it.wallpapers.filter { it == it1 }, + ) + ) + albumRepository.deleteWallpaper(it1) + } + } else { + selectedRepository.upsertSelectedAlbum( + it.copy( + album = it.album.copy( + homeWallpapersInQueue = if (setHomeOrLock) it.album.homeWallpapersInQueue.drop(1) else it.album.homeWallpapersInQueue, + lockWallpapersInQueue = if (!setHomeOrLock) it.album.lockWallpapersInQueue.drop(1) else it.album.lockWallpapersInQueue, + currentHomeWallpaper = if (setHomeOrLock) wallpaper else it.album.currentHomeWallpaper, + currentLockWallpaper = if (!setHomeOrLock) wallpaper else it.album.currentLockWallpaper + ) + ) + ) + } + } + // No more wallpapers in the queue -- reshuffle the wallpapers + else { + val newWallpaperInQueue = it.wallpapers.map { it.wallpaperUri }.shuffled() + wallpaper = newWallpaperInQueue.firstOrNull() + if (wallpaper != null) { + if (!setWallpaper( + context = context, + wallpaper = wallpaper.toUri(), + darken = darken, + darkenPercent = darkenPercentage, + scaling = scaling, + setHome = setHome, + setLock = setLock, + setLockOrHome = setHomeOrLock, + blur = blur, + blurPercent = blurPercentage + ) + ) { + selectedAlbum.wallpapers.firstOrNull { it.wallpaperUri == wallpaper } + ?.let { it1 -> + selectedRepository.deleteWallpaper(it1) + selectedRepository.upsertSelectedAlbum( + it.copy( + album = it.album.copy( + homeWallpapersInQueue = if (setHomeOrLock) newWallpaperInQueue.drop(1) else it.album.homeWallpapersInQueue, + lockWallpapersInQueue = if (!setHomeOrLock) newWallpaperInQueue.drop(1) else it.album.lockWallpapersInQueue, + currentHomeWallpaper = if (setHomeOrLock) wallpaper else it.album.currentHomeWallpaper, + currentLockWallpaper = if (!setHomeOrLock) wallpaper else it.album.currentLockWallpaper + ), + wallpapers = it.wallpapers.filter { it == it1 } + ) + ) + albumRepository.deleteWallpaper(it1) + } + } else { + selectedRepository.upsertSelectedAlbum( + it.copy( + album = it.album.copy( + homeWallpapersInQueue = if (setHomeOrLock) newWallpaperInQueue.drop(1) else it.album.homeWallpapersInQueue, + lockWallpapersInQueue = if (!setHomeOrLock) newWallpaperInQueue.drop(1) else it.album.lockWallpapersInQueue, + currentHomeWallpaper = if (setHomeOrLock) wallpaper else it.album.currentHomeWallpaper, + currentLockWallpaper = if (!setHomeOrLock) wallpaper else it.album.currentLockWallpaper + ) + ) + ) + } + } else { onDestroy() } + } + } + else { + var wallpaper = it.album.homeWallpapersInQueue.firstOrNull() + if (wallpaper != null) { + if (!setWallpaper( + context = context, + wallpaper = wallpaper.toUri(), + darken = darken, + darkenPercent = darkenPercentage, + scaling = scaling, + setHome = setHome, + setLock = setLock, + blur = blur, + blurPercent = blurPercentage + )) { + selectedAlbum.wallpapers.firstOrNull { it.wallpaperUri == wallpaper } + ?.let { it1 -> + selectedRepository.deleteWallpaper(it1) + selectedRepository.upsertSelectedAlbum( + it.copy( + album = it.album.copy( + homeWallpapersInQueue = it.album.homeWallpapersInQueue.drop(1), + lockWallpapersInQueue = it.album.lockWallpapersInQueue.filter { it != wallpaper }, + currentHomeWallpaper = null, + currentLockWallpaper = null + ), + wallpapers = it.wallpapers.filter { it == it1 }, + ) + ) + albumRepository.deleteWallpaper(it1) + } + } else { + selectedRepository.upsertSelectedAlbum( + it.copy( + album = it.album.copy( + homeWallpapersInQueue = it.album.homeWallpapersInQueue.drop(1), + lockWallpapersInQueue = it.album.lockWallpapersInQueue.filter { it != wallpaper }, + currentHomeWallpaper = wallpaper, + currentLockWallpaper = wallpaper + ) + ) + ) + } + } + // No more wallpapers in the queue -- reshuffle the wallpapers + else { + val newWallpaperInQueue = it.wallpapers.map { it.wallpaperUri }.shuffled() + wallpaper = newWallpaperInQueue.firstOrNull() + if (wallpaper != null) { + if (!setWallpaper( + context = context, + wallpaper = wallpaper.toUri(), + darken = darken, + darkenPercent = darkenPercentage, + scaling = scaling, + setHome = setHome, + setLock = setLock, + setLockOrHome = setHomeOrLock, + blur = blur, + blurPercent = blurPercentage + ) + ) { + selectedAlbum.wallpapers.firstOrNull { it.wallpaperUri == wallpaper } + ?.let { it1 -> + selectedRepository.deleteWallpaper(it1) + selectedRepository.upsertSelectedAlbum( + it.copy( + album = it.album.copy( + homeWallpapersInQueue = it.album.homeWallpapersInQueue.drop(1), + lockWallpapersInQueue = it.album.lockWallpapersInQueue.filter { it != wallpaper }, + currentHomeWallpaper = null, + currentLockWallpaper = null + ), + wallpapers = it.wallpapers.filter { it == it1 } + ) + ) + albumRepository.deleteWallpaper(it1) + } + } else { + selectedRepository.upsertSelectedAlbum( + it.copy( + album = it.album.copy( + homeWallpapersInQueue = newWallpaperInQueue.drop(1), + currentHomeWallpaper = wallpaper, + currentLockWallpaper = wallpaper + ) + ) + ) + } + } else { onDestroy() } + } + } + } + } + } catch (e: Exception) { + Log.e("PaperizeWallpaperChanger", "Error in changing wallpaper", e) + } + } + + private suspend fun updateCurrentWallpaper(context: Context) { + try { + val selectedAlbum = selectedRepository.getSelectedAlbum().first().firstOrNull() + if (selectedAlbum == null) { + onDestroy() + return + } + else { + val setHome = settingsDataStoreImpl.getBoolean(SettingsConstants.HOME_WALLPAPER) ?: false + val setLock = settingsDataStoreImpl.getBoolean(SettingsConstants.LOCK_WALLPAPER) ?: false + if (!setHome && !setLock) { + onDestroy() + return + } + val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) + val time = LocalDateTime.now() + settingsDataStoreImpl.putString(SettingsConstants.LAST_SET_TIME, time.format(formatter)) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, time.plusMinutes(timeInMinutes1.toLong()).format(formatter)) + val scaling = settingsDataStoreImpl.getString(SettingsConstants.WALLPAPER_SCALING)?.let { ScalingConstants.valueOf(it) } ?: ScalingConstants.FILL + val darken = settingsDataStoreImpl.getBoolean(SettingsConstants.DARKEN) ?: false + val darkenPercentage = settingsDataStoreImpl.getInt(SettingsConstants.DARKEN_PERCENTAGE) ?: 100 + val blur = settingsDataStoreImpl.getBoolean(SettingsConstants.BLUR) ?: false + val blurPercentage = settingsDataStoreImpl.getInt(SettingsConstants.BLUR_PERCENTAGE) ?: 0 + + selectedAlbum.let { it -> + val wallpaper1 = it.album.currentHomeWallpaper + val wallpaper2 = it.album.currentLockWallpaper + if (wallpaper1 != null) { + if (!setWallpaper( + context = context, + wallpaper = wallpaper1.toUri(), + darken = darken, + darkenPercent = darkenPercentage, + scaling = scaling, + setHome = setHome, + setLock = setLock, + setLockOrHome = true, + blur = blur, + blurPercent = blurPercentage + )) { + selectedAlbum.wallpapers.firstOrNull{ it.wallpaperUri == wallpaper1 } + ?.let { it1 -> + selectedRepository.deleteWallpaper(it1) + selectedRepository.upsertSelectedAlbum( + it.copy( + album = it.album.copy( + homeWallpapersInQueue = it.album.homeWallpapersInQueue.filter { it != wallpaper1 }, + lockWallpapersInQueue = it.album.lockWallpapersInQueue.filter { it != wallpaper1 }, + currentHomeWallpaper = null + ), + wallpapers = it.wallpapers.filter { it == it1 }, + ) + ) + albumRepository.deleteWallpaper(it1) + } + } + } + if (wallpaper2 != null) { + if (!setWallpaper( + context = context, + wallpaper = wallpaper2.toUri(), + darken = darken, + darkenPercent = darkenPercentage, + scaling = scaling, + setHome = setHome, + setLock = setLock, + setLockOrHome = false, + blur = blur, + blurPercent = blurPercentage + )) { + selectedAlbum.wallpapers.firstOrNull{ it.wallpaperUri == wallpaper2 } + ?.let { it1 -> + selectedRepository.deleteWallpaper(it1) + selectedRepository.upsertSelectedAlbum( + it.copy( + album = it.album.copy( + homeWallpapersInQueue = it.album.homeWallpapersInQueue.filter { it != wallpaper1 }, + lockWallpapersInQueue = it.album.lockWallpapersInQueue.filter { it != wallpaper1 }, + currentLockWallpaper = null + ), + wallpapers = it.wallpapers.filter { it == it1 }, + ) + ) + albumRepository.deleteWallpaper(it1) + } + } + } + } + } + } catch (e: Exception) { + Log.e("PaperizeWallpaperChanger", "Error in updating", e) + } + } + + /** + * Sets the wallpaper to the given uri + */ + private fun setWallpaper( + context: Context, + wallpaper: Uri, + darken: Boolean, + darkenPercent: Int, + scaling: ScalingConstants, + setHome: Boolean, setLock: Boolean, + setLockOrHome: Boolean? = null, + blur: Boolean = false, + blurPercent: Int + ): Boolean { + val wallpaperManager = WallpaperManager.getInstance(context) + try { + val imageSize = wallpaper.getImageDimensions(context) ?: return false + val aspectRatio = imageSize.height.toFloat() / imageSize.width.toFloat() + val device = context.resources.displayMetrics + val targetWidth = min(4 * device.widthPixels, imageSize.width) + val targetHeight = (targetWidth * aspectRatio).toInt() + + val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + val source = ImageDecoder.createSource(context.contentResolver, wallpaper) + ImageDecoder.decodeBitmap(source) { decoder, _, _ -> + decoder.setTargetSize(targetWidth, targetHeight) + decoder.isMutableRequired = true + } + } + else { + context.contentResolver.openInputStream(wallpaper)?.use { inputStream -> + val options = BitmapFactory.Options().apply { + inSampleSize = calculateInSampleSize(imageSize, targetWidth, targetHeight) + inMutable = true + } + BitmapFactory.decodeStream(inputStream, null, options) + } + } + + if (bitmap == null) return false + else { + processBitmap(device, bitmap, darken, darkenPercent, scaling, blur, blurPercent)?.let { image -> + when { + setLockOrHome == true -> wallpaperManager.setBitmap(image, null, true, WallpaperManager.FLAG_SYSTEM) + setLockOrHome == false -> wallpaperManager.setBitmap(image, null, true, WallpaperManager.FLAG_LOCK) + setHome && setLock -> wallpaperManager.setBitmap(image, null, true, WallpaperManager.FLAG_LOCK or WallpaperManager.FLAG_SYSTEM) + setHome -> wallpaperManager.setBitmap(image, null, true, WallpaperManager.FLAG_SYSTEM) + setLock -> wallpaperManager.setBitmap(image, null, true, WallpaperManager.FLAG_LOCK) + else -> {} + } + image.recycle() + } + bitmap.recycle() + return true + } + } catch (e: IOException) { + Log.e("PaperizeWallpaperChanger", "Error setting wallpaper", e) + return false + } + } + + /** + * Darkens the bitmap by the given percentage and returns it + * 0 - lightest, 100 - darkest + */ + private fun processBitmap( + device: DisplayMetrics, + source: Bitmap, darken: Boolean, + darkenPercent: Int, + scaling: ScalingConstants, + blur: Boolean, + blurPercent: Int + ): Bitmap? { + try { + var processedBitmap = source + + // Apply wallpaper scaling effects + processedBitmap = when (scaling) { + ScalingConstants.FILL -> fillBitmap(processedBitmap, device.widthPixels, device.heightPixels) + ScalingConstants.FIT -> fitBitmap(processedBitmap, device.widthPixels, device.heightPixels) + ScalingConstants.STRETCH -> stretchBitmap(processedBitmap, device.widthPixels, device.heightPixels) + } + + // Apply brightness effect + if (darken && darkenPercent < 100) { + processedBitmap = darkenBitmap(processedBitmap, darkenPercent) + } + + // Apply blur effect + if (blur && blurPercent > 0) { + processedBitmap = blurBitmap(processedBitmap, blurPercent) + } + return processedBitmap + } catch (e: Exception) { + Log.e("PaperizeWallpaperChanger", "Error darkening bitmap", e) + return null + } + } + + private fun refreshAlbum(context: Context) { + CoroutineScope(Dispatchers.IO).launch { + try { + var albumWithWallpapers = albumRepository.getAlbumsWithWallpaperAndFolder().first() + albumWithWallpapers.forEach { albumWithWallpaper -> + // Delete wallpaper if the URI is invalid + val invalidWallpapers = albumWithWallpaper.wallpapers.filterNot { wallpaper -> + val file = DocumentFile.fromSingleUri(context, wallpaper.wallpaperUri.toUri()) + file?.exists() == true + } + if (invalidWallpapers.isNotEmpty()) { + albumRepository.deleteWallpaperList(invalidWallpapers) + } + + // Update folder wallpapers + albumWithWallpaper.folders.forEach { folder -> + DocumentFileCompat.fromTreeUri(context, folder.folderUri.toUri())?.let { folderDirectory -> + if (!folderDirectory.isDirectory()) { + albumRepository.deleteFolder(folder) + } else { + val wallpapers = getWallpaperFromFolder(folder.folderUri, context) + albumRepository.updateFolder(folder.copy(wallpapers = wallpapers)) + } + } + } + + // Delete empty albums + if (albumWithWallpaper.wallpapers.isEmpty() && albumWithWallpaper.folders.all { it.wallpapers.isEmpty() }) { + albumRepository.deleteAlbum(albumWithWallpaper.album) + } + } + + // Update selected album + albumWithWallpapers = albumRepository.getAlbumsWithWallpaperAndFolder().first() + val selectedAlbum = selectedRepository.getSelectedAlbum().first().firstOrNull() + if (selectedAlbum != null) { + albumWithWallpapers.find { it.album.initialAlbumName == selectedAlbum.album.initialAlbumName } + ?.let { foundAlbum -> + val albumNameHashCode = foundAlbum.album.initialAlbumName.hashCode() + val wallpapers: List = + foundAlbum.wallpapers + foundAlbum.folders.flatMap { folder -> + folder.wallpapers.map { wallpaper -> + Wallpaper( + initialAlbumName = foundAlbum.album.initialAlbumName, + wallpaperUri = wallpaper, + key = wallpaper.hashCode() + albumNameHashCode, + ) + } + } + val wallpapersUri = wallpapers.map { it.wallpaperUri }.toSet() + if (wallpapersUri.isEmpty()) { + selectedRepository.deleteAll() + onDestroy() + } + else { + val newSelectedAlbum = SelectedAlbum( + album = foundAlbum.album.copy( + homeWallpapersInQueue = wallpapersUri.shuffled(), + lockWallpapersInQueue = wallpapersUri.shuffled(), + currentHomeWallpaper = selectedAlbum.album.currentHomeWallpaper, + currentLockWallpaper = selectedAlbum.album.currentLockWallpaper, + ), + wallpapers = wallpapers + ) + selectedRepository.upsertSelectedAlbum(newSelectedAlbum) + } + } ?: run { onDestroy() } + } + } catch (e: Exception) { + Log.e("PaperizeWallpaperChanger", "Error refreshing album", e) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt similarity index 70% rename from app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService.kt rename to app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt index 4deea665..cf205e95 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt @@ -13,8 +13,8 @@ import android.graphics.ImageDecoder import android.net.Uri import android.os.Build import android.os.Handler +import android.os.HandlerThread import android.os.IBinder -import android.os.Looper import android.util.DisplayMetrics import android.util.Log import androidx.core.app.NotificationCompat @@ -51,29 +51,26 @@ import java.time.format.FormatStyle import javax.inject.Inject import kotlin.math.min - +/** + * Used in conjunction with [WallpaperService1] to schedule home screen and lock screen separately + */ @AndroidEntryPoint -class WallpaperService: Service() { +class WallpaperService2: Service() { + private enum class Type { HOME, LOCK, BOTH } + private val handleThread = HandlerThread("MyThread2") + private lateinit var workerHandler: Handler @Inject lateinit var selectedRepository: SelectedAlbumRepository @Inject lateinit var albumRepository: AlbumRepository @Inject lateinit var settingsDataStoreImpl: SettingsDataStore - private val handler1 = Handler(Looper.getMainLooper()) - private val handler2 = Handler(Looper.getMainLooper()) - private lateinit var runnableCode1: Runnable - private lateinit var runnableCode2: Runnable + private var scheduleSeparately: Boolean = false private var timeInMinutes1: Int = SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT private var timeInMinutes2: Int = SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT private var nextSetTime1: LocalDateTime? = null private var nextSetTime2: LocalDateTime? = null - private var scheduleSeparately: Boolean = false - private var lastRan1: LocalDateTime? = null - private var lastRan2: LocalDateTime? = null - private var refresherTimer = LocalDateTime.now() enum class Actions { START, REQUEUE, - STOP, UPDATE } @@ -81,291 +78,116 @@ class WallpaperService: Service() { return null } - /** - * Set up the runnable code to change the wallpaper to prevent runnableCode from being null - */ override fun onCreate() { super.onCreate() - startForeground(1, createNotification()) - runnableCode1 = object : Runnable { - override fun run() { - val self = this - CoroutineScope(Dispatchers.IO).launch { - try { - if (LocalDateTime.now().minusDays(1).isAfter(refresherTimer)) { - refreshAlbum(this@WallpaperService) - refresherTimer = LocalDateTime.now() - delay(5000) + handleThread.start() + workerHandler = Handler(handleThread.looper) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + if (intent != null) { + when (intent.action) { + Actions.START.toString() -> { + timeInMinutes1 = intent.getIntExtra("timeInMinutes1", SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT) + timeInMinutes2 = intent.getIntExtra("timeInMinutes2", SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT) + scheduleSeparately = intent.getBooleanExtra("scheduleSeparately", false) + val setHomeOrLock = if (scheduleSeparately) { + when (intent.getIntExtra("type", Type.BOTH.ordinal)) { + Type.HOME.ordinal -> true + Type.LOCK.ordinal -> false + else -> null } - changeWallpaper(this@WallpaperService, true) - } catch (e: Exception) { - Log.e("PaperizeWallpaperChanger", "Error in runnableCode", e) - } finally { - handler1.postDelayed(self, timeInMinutes1 * 60 * 1000L) - } + } else { null } + workerTaskStart(setHomeOrLock) } - } - } - runnableCode2 = object : Runnable { - override fun run() { - val self = this - CoroutineScope(Dispatchers.IO).launch { - try { - if (LocalDateTime.now().minusDays(1).isAfter(refresherTimer)) { - refreshAlbum(this@WallpaperService) - refresherTimer = LocalDateTime.now() - delay(5000) - } - changeWallpaper(this@WallpaperService, false) - } catch (e: Exception) { - Log.e("PaperizeWallpaperChanger", "Error in runnableCode", e) - } finally { - handler2.postDelayed(self, timeInMinutes2 * 60 * 1000L + 10000) - } + Actions.REQUEUE.toString() -> { + timeInMinutes1 = intent.getIntExtra("timeInMinutes1", SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT) + timeInMinutes2 = intent.getIntExtra("timeInMinutes2", SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT) + scheduleSeparately = intent.getBooleanExtra("scheduleSeparately", false) + workerTaskRequeue() + } + Actions.UPDATE.toString() -> { + workerTaskUpdate() } } } + return START_NOT_STICKY } - /** - * Start the service and schedule the wallpaper change - */ - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - when(intent?.action) { - Actions.START.toString() -> { - handler1.removeCallbacks(runnableCode1) - handler2.removeCallbacks(runnableCode2) - timeInMinutes1 = intent.getIntExtra("timeInMinutes1", SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT) - timeInMinutes2 = intent.getIntExtra("timeInMinutes2", SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT) - scheduleSeparately = intent.getBooleanExtra("scheduleSeparately", false) - nextSetTime1 = null - nextSetTime2 = null - lastRan1 = null - lastRan2 = null - refresherTimer = LocalDateTime.now() + override fun onDestroy() { + super.onDestroy() + workerHandler.removeCallbacksAndMessages(null) + handleThread.quitSafely() + } - if (!scheduleSeparately) { - runnableCode1 = object : Runnable { - override fun run() { - val self = this - CoroutineScope(Dispatchers.IO).launch { - try { - if (LocalDateTime.now().minusDays(1).isAfter(refresherTimer)) { - refreshAlbum(this@WallpaperService) - refresherTimer = LocalDateTime.now() - delay(5000) - } - changeWallpaper(this@WallpaperService, null) - } catch (e: Exception) { - Log.e("PaperizeWallpaperChanger", "Error in runnableCode", e) - } finally { - handler1.postDelayed(self, timeInMinutes1 * 60 * 1000L) - } - } - } - } - handler1.postDelayed(runnableCode1, 1000) - } - else { - runnableCode1 = object : Runnable { - override fun run() { - val self = this - CoroutineScope(Dispatchers.IO).launch { - try { - if (LocalDateTime.now().minusDays(1).isAfter(refresherTimer)) { - refreshAlbum(this@WallpaperService) - refresherTimer = LocalDateTime.now() - delay(5000) - } - changeWallpaper(this@WallpaperService, true) - } catch (e: Exception) { - Log.e("PaperizeWallpaperChanger", "Error in runnableCode", e) - } finally { - handler1.postDelayed(self, timeInMinutes1 * 60 * 1000L) - } - } - } - } - handler1.postDelayed(runnableCode1, 1000) - runnableCode2 = object : Runnable { - override fun run() { - val self = this - CoroutineScope(Dispatchers.IO).launch { - try { - if (LocalDateTime.now().minusDays(1).isAfter(refresherTimer)) { - refreshAlbum(this@WallpaperService) - refresherTimer = LocalDateTime.now() - delay(5000) - } - changeWallpaper(this@WallpaperService, false) - } catch (e: Exception) { - Log.e("PaperizeWallpaperChanger", "Error in runnableCode", e) - } finally { - handler2.postDelayed(self, timeInMinutes2 * 60 * 1000L + 10000) - } - } - } - } - handler2.postDelayed(runnableCode2, 5000) - } + private fun workerTaskStart(setHomeOrLock: Boolean? = null) { + workerHandler.post { + CoroutineScope(Dispatchers.IO).launch { + delay(5 * 1000) + changeWallpaper(this@WallpaperService2, setHomeOrLock) } - Actions.REQUEUE.toString() -> { - handler1.removeCallbacks(runnableCode1) - handler2.removeCallbacks(runnableCode2) - timeInMinutes1 = intent.getIntExtra("timeInMinutes1", SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT) - timeInMinutes2 = intent.getIntExtra("timeInMinutes2", SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT) - scheduleSeparately = intent.getBooleanExtra("scheduleSeparately", false) - nextSetTime1 = null - nextSetTime2 = null - lastRan1 = null - lastRan2 = null - refresherTimer = LocalDateTime.now() - - val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) - val currentTime = LocalDateTime.now() - CoroutineScope(Dispatchers.IO).launch { - settingsDataStoreImpl.putString(SettingsConstants.LAST_SET_TIME, currentTime.format(formatter)) - if (scheduleSeparately) { - nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) - nextSetTime2 = currentTime.plusMinutes(timeInMinutes2.toLong()) - val earliestTime = (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2)!!.format(formatter) - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, earliestTime) - } - else { - nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) - nextSetTime2 = nextSetTime1 - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, currentTime.plusMinutes(timeInMinutes1.toLong()).format(formatter)) - } - } + stopSelf() + } + } + private fun workerTaskRequeue() { + workerHandler.post { + CoroutineScope(Dispatchers.IO).launch { + nextSetTime1 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_1)) + nextSetTime2 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_2)) val notification = createNotification() val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.notify(1, notification) - if (!scheduleSeparately) { - runnableCode1 = object : Runnable { - override fun run() { - val self = this - CoroutineScope(Dispatchers.IO).launch { - try { - if (LocalDateTime.now().minusDays(1).isAfter(refresherTimer)) { - refreshAlbum(this@WallpaperService) - refresherTimer = LocalDateTime.now() - delay(5000) - } - changeWallpaper(this@WallpaperService, null) - } catch (e: Exception) { - Log.e("PaperizeWallpaperChanger", "Error in runnableCode", e) - } finally { - handler1.postDelayed(self, timeInMinutes1 * 60 * 1000L) - } - } - } - } - handler1.postDelayed(runnableCode1, timeInMinutes1 * 60 * 1000L) + notification?.let { notificationManager.notify(1, it) } + val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) + val currentTime = LocalDateTime.now() + settingsDataStoreImpl.putString(SettingsConstants.LAST_SET_TIME, currentTime.format(formatter)) + if (scheduleSeparately) { + nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) + nextSetTime2 = currentTime.plusMinutes(timeInMinutes2.toLong()) + val earliestTime = (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2)!!.format(formatter) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, earliestTime) } else { - runnableCode1 = object : Runnable { - override fun run() { - val self = this - CoroutineScope(Dispatchers.IO).launch { - try { - if (LocalDateTime.now().minusDays(1).isAfter(refresherTimer)) { - refreshAlbum(this@WallpaperService) - refresherTimer = LocalDateTime.now() - delay(5000) - } - changeWallpaper(this@WallpaperService, true) - } catch (e: Exception) { - Log.e("PaperizeWallpaperChanger", "Error in runnableCode", e) - } finally { - handler1.postDelayed(self, timeInMinutes1 * 60 * 1000L) - } - } - } - } - handler1.postDelayed(runnableCode1, timeInMinutes1 * 60 * 1000L) - runnableCode2 = object : Runnable { - override fun run() { - val self = this - CoroutineScope(Dispatchers.IO).launch { - try { - if (LocalDateTime.now().minusDays(1).isAfter(refresherTimer)) { - refreshAlbum(this@WallpaperService) - refresherTimer = LocalDateTime.now() - delay(5000) - } - changeWallpaper(this@WallpaperService, false) - } catch (e: Exception) { - Log.e("PaperizeWallpaperChanger", "Error in runnableCode", e) - } finally { - handler2.postDelayed(self, timeInMinutes2 * 60 * 1000L + 10000) - } - } - } - } - handler2.postDelayed(runnableCode2, timeInMinutes2 * 60 * 1000L + 10000) - } - } - Actions.UPDATE.toString() -> { - CoroutineScope(Dispatchers.IO).launch { - try { - updateCurrentWallpaper(this@WallpaperService) - } catch (e: Exception) { - Log.e("PaperizeWallpaperChanger", "Error in changing brightness", e) - } - } - } - Actions.STOP.toString() -> { - handler1.removeCallbacks(runnableCode1) - handler2.removeCallbacks(runnableCode2) - CoroutineScope(Dispatchers.IO).launch { - settingsDataStoreImpl.putString(SettingsConstants.LAST_SET_TIME, "") - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, "") + nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) + nextSetTime2 = nextSetTime1 + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, currentTime.plusMinutes(timeInMinutes1.toLong()).format(formatter)) } - stopSelf() } + stopSelf() } - return START_STICKY } - /** - * Clean up the service when it is destroyed - */ - override fun onDestroy() { - super.onDestroy() - handler1.removeCallbacks(runnableCode1) - handler2.removeCallbacks(runnableCode2) - CoroutineScope(Dispatchers.IO).launch { - settingsDataStoreImpl.putString(SettingsConstants.LAST_SET_TIME, "") - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, "") + private fun workerTaskUpdate() { + workerHandler.post { + CoroutineScope(Dispatchers.IO).launch { + updateCurrentWallpaper(this@WallpaperService2) + } + stopSelf() } } /** * Creates a notification for the wallpaper service */ - private fun createNotification(): Notification { + private fun createNotification(): Notification? { val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) - var earliestTime = when { - nextSetTime1 != null && nextSetTime2 != null -> (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2)!!.format(formatter) + val earliestTime = when { + nextSetTime1 != null && nextSetTime2 != null -> (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2)?.format(formatter) nextSetTime1 != null -> nextSetTime1!!.format(formatter) nextSetTime2 != null -> nextSetTime2!!.format(formatter) - else -> LocalDateTime.now().format(formatter) + else -> null } - if (earliestTime != null) { // Edge case where numbers like 2131689515 pass through - if (earliestTime.length <= 10) { - earliestTime = LocalDateTime.now().format(formatter) - } + if (earliestTime != null) { + val intent = Intent(this, MainActivity::class.java) + val pendingIntent = PendingIntent.getActivity(this, 3, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + return NotificationCompat.Builder(this, "wallpaper_service_channel") + .setContentTitle(getString(R.string.app_name)) + .setContentText(getString(R.string.next_wallpaper_change, earliestTime)) + .setSmallIcon(R.drawable.notification_icon) + .setContentIntent(pendingIntent) + .build() } - - val intent = Intent(this, MainActivity::class.java) - val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) - return NotificationCompat.Builder(this, "wallpaper_service_channel") - .setContentTitle(getString(R.string.app_name)) - .setContentText(getString(R.string.next_wallpaper_change, earliestTime)) - .setSmallIcon(R.drawable.notification_icon) - .setContentIntent(pendingIntent) - .build() + return null } /** @@ -383,6 +205,12 @@ class WallpaperService: Service() { val toggled = settingsDataStoreImpl.getBoolean(SettingsConstants.ENABLE_CHANGER) ?: false val setHome = settingsDataStoreImpl.getBoolean(SettingsConstants.HOME_WALLPAPER) ?: false val setLock = settingsDataStoreImpl.getBoolean(SettingsConstants.LOCK_WALLPAPER) ?: false + nextSetTime1 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_1)) + nextSetTime2 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_2)) + val notification = createNotification() + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notification?.let { notificationManager.notify(1, it) } + if (!toggled || (!setHome && !setLock)) { onDestroy() return @@ -411,9 +239,8 @@ class WallpaperService: Service() { } } - val notification = createNotification() - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.notify(1, notification) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_1, nextSetTime1.toString()) + settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_2, nextSetTime2.toString()) val scaling = settingsDataStoreImpl.getString(SettingsConstants.WALLPAPER_SCALING)?.let { ScalingConstants.valueOf(it) } ?: ScalingConstants.FILL val darken = settingsDataStoreImpl.getBoolean(SettingsConstants.DARKEN) ?: false @@ -700,11 +527,6 @@ class WallpaperService: Service() { } } } - if (wallpaper1 != null || wallpaper2 != null) { - val notification = createNotification() - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.notify(1, notification) - } } } } catch (e: Exception) { From 87ef7ebaf07bc4a803892a28e7b7efb6272e0b66 Mon Sep 17 00:00:00 2001 From: Anthonyy232 Date: Sun, 2 Jun 2024 12:43:46 -0700 Subject: [PATCH 3/8] Fixed notifications showing wrong time --- .../wallpaper_service/WallpaperService1.kt | 56 ++++++------------- .../wallpaper_service/WallpaperService2.kt | 45 +++++---------- 2 files changed, 31 insertions(+), 70 deletions(-) diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt index fb64eecc..5f7c58fe 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt @@ -66,6 +66,7 @@ class WallpaperService1: Service() { private var timeInMinutes2: Int = SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT private var nextSetTime1: LocalDateTime? = null private var nextSetTime2: LocalDateTime? = null + private var nextSetTime: LocalDateTime? = null enum class Actions { START, @@ -133,25 +134,10 @@ class WallpaperService1: Service() { CoroutineScope(Dispatchers.IO).launch { nextSetTime1 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_1)) nextSetTime2 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_2)) + nextSetTime = (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2) val notification = createNotification() val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notification?.let { notificationManager.notify(1, it) } - val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) - val currentTime = LocalDateTime.now() - settingsDataStoreImpl.putString(SettingsConstants.LAST_SET_TIME, currentTime.format(formatter)) - if (scheduleSeparately) { - nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) - nextSetTime2 = currentTime.plusMinutes(timeInMinutes2.toLong()) - val earliestTime = (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2)!!.format(formatter) - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, earliestTime) - } - else { - nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) - nextSetTime2 = nextSetTime1 - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, currentTime.plusMinutes(timeInMinutes1.toLong()).format(formatter)) - } - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_1, nextSetTime1.toString()) - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_2, nextSetTime2.toString()) } stopSelf() } @@ -171,18 +157,12 @@ class WallpaperService1: Service() { */ private fun createNotification(): Notification? { val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) - val earliestTime = when { - nextSetTime1 != null && nextSetTime2 != null -> (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2)?.format(formatter) - nextSetTime1 != null -> nextSetTime1!!.format(formatter) - nextSetTime2 != null -> nextSetTime2!!.format(formatter) - else -> null - } - if (earliestTime != null) { + if (nextSetTime != null) { val intent = Intent(this, MainActivity::class.java) val pendingIntent = PendingIntent.getActivity(this, 3, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) return NotificationCompat.Builder(this, "wallpaper_service_channel") .setContentTitle(getString(R.string.app_name)) - .setContentText(getString(R.string.next_wallpaper_change, earliestTime)) + .setContentText(getString(R.string.next_wallpaper_change, nextSetTime!!.format(formatter))) .setSmallIcon(R.drawable.notification_icon) .setContentIntent(pendingIntent) .build() @@ -207,10 +187,6 @@ class WallpaperService1: Service() { val setLock = settingsDataStoreImpl.getBoolean(SettingsConstants.LOCK_WALLPAPER) ?: false nextSetTime1 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_1)) nextSetTime2 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_2)) - val notification = createNotification() - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notification?.let { notificationManager.notify(1, it) } - if (!toggled || (!setHome && !setLock)) { onDestroy() return @@ -222,26 +198,28 @@ class WallpaperService1: Service() { if (setHomeOrLock == null) { nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) nextSetTime2 = nextSetTime1 - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, currentTime.plusMinutes(timeInMinutes1.toLong()).format(formatter)) + nextSetTime = nextSetTime1 + nextSetTime?.let { settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, it.format(formatter)) } } else { if (setHomeOrLock) { nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) } else { nextSetTime2 = currentTime.plusMinutes(timeInMinutes2.toLong()) } - if (nextSetTime1 == null && nextSetTime2 != null) { - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, nextSetTime2!!.format(formatter)) - } - else if (nextSetTime1 != null && nextSetTime2 == null) { - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, nextSetTime1!!.format(formatter)) - } - else { - val earliestTime = if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2 - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, earliestTime!!.format(formatter)) + nextSetTime = if (nextSetTime1 == null && nextSetTime2 != null) { + nextSetTime2 + } else if (nextSetTime1 != null && nextSetTime2 == null) { + nextSetTime1 + } else { + if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2 } + nextSetTime?.let { settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, it.format(formatter)) } } - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_1, nextSetTime1.toString()) settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_2, nextSetTime2.toString()) + val notification = createNotification() + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notification?.let { notificationManager.notify(1, it) } + val scaling = settingsDataStoreImpl.getString(SettingsConstants.WALLPAPER_SCALING)?.let { ScalingConstants.valueOf(it) } ?: ScalingConstants.FILL val darken = settingsDataStoreImpl.getBoolean(SettingsConstants.DARKEN) ?: false val darkenPercentage = settingsDataStoreImpl.getInt(SettingsConstants.DARKEN_PERCENTAGE) ?: 100 diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt index cf205e95..9c6ff7f3 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt @@ -67,6 +67,7 @@ class WallpaperService2: Service() { private var timeInMinutes2: Int = SettingsConstants.WALLPAPER_CHANGE_INTERVAL_DEFAULT private var nextSetTime1: LocalDateTime? = null private var nextSetTime2: LocalDateTime? = null + private var nextSetTime: LocalDateTime? = null enum class Actions { START, @@ -135,23 +136,10 @@ class WallpaperService2: Service() { CoroutineScope(Dispatchers.IO).launch { nextSetTime1 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_1)) nextSetTime2 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_2)) + nextSetTime = (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2) val notification = createNotification() val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notification?.let { notificationManager.notify(1, it) } - val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) - val currentTime = LocalDateTime.now() - settingsDataStoreImpl.putString(SettingsConstants.LAST_SET_TIME, currentTime.format(formatter)) - if (scheduleSeparately) { - nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) - nextSetTime2 = currentTime.plusMinutes(timeInMinutes2.toLong()) - val earliestTime = (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2)!!.format(formatter) - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, earliestTime) - } - else { - nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) - nextSetTime2 = nextSetTime1 - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, currentTime.plusMinutes(timeInMinutes1.toLong()).format(formatter)) - } } stopSelf() } @@ -171,18 +159,12 @@ class WallpaperService2: Service() { */ private fun createNotification(): Notification? { val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) - val earliestTime = when { - nextSetTime1 != null && nextSetTime2 != null -> (if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2)?.format(formatter) - nextSetTime1 != null -> nextSetTime1!!.format(formatter) - nextSetTime2 != null -> nextSetTime2!!.format(formatter) - else -> null - } - if (earliestTime != null) { + if (nextSetTime != null) { val intent = Intent(this, MainActivity::class.java) val pendingIntent = PendingIntent.getActivity(this, 3, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) return NotificationCompat.Builder(this, "wallpaper_service_channel") .setContentTitle(getString(R.string.app_name)) - .setContentText(getString(R.string.next_wallpaper_change, earliestTime)) + .setContentText(getString(R.string.next_wallpaper_change, nextSetTime!!.format(formatter))) .setSmallIcon(R.drawable.notification_icon) .setContentIntent(pendingIntent) .build() @@ -207,10 +189,6 @@ class WallpaperService2: Service() { val setLock = settingsDataStoreImpl.getBoolean(SettingsConstants.LOCK_WALLPAPER) ?: false nextSetTime1 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_1)) nextSetTime2 = LocalDateTime.parse(settingsDataStoreImpl.getString(SettingsConstants.NEXT_SET_TIME_2)) - val notification = createNotification() - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notification?.let { notificationManager.notify(1, it) } - if (!toggled || (!setHome && !setLock)) { onDestroy() return @@ -222,26 +200,31 @@ class WallpaperService2: Service() { if (setHomeOrLock == null) { nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) nextSetTime2 = nextSetTime1 - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, currentTime.plusMinutes(timeInMinutes1.toLong()).format(formatter)) + nextSetTime = nextSetTime1 + nextSetTime?.let { settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, it.format(formatter)) } } else { if (setHomeOrLock) { nextSetTime1 = currentTime.plusMinutes(timeInMinutes1.toLong()) } else { nextSetTime2 = currentTime.plusMinutes(timeInMinutes2.toLong()) } if (nextSetTime1 == null && nextSetTime2 != null) { - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, nextSetTime2!!.format(formatter)) + nextSetTime = nextSetTime2 } else if (nextSetTime1 != null && nextSetTime2 == null) { - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, nextSetTime1!!.format(formatter)) + nextSetTime = nextSetTime1 } else { - val earliestTime = if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2 - settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, earliestTime!!.format(formatter)) + nextSetTime = if (nextSetTime1!!.isBefore(nextSetTime2)) nextSetTime1 else nextSetTime2 } + nextSetTime?.let { settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME, it.format(formatter)) } } settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_1, nextSetTime1.toString()) settingsDataStoreImpl.putString(SettingsConstants.NEXT_SET_TIME_2, nextSetTime2.toString()) + val notification = createNotification() + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notification?.let { notificationManager.notify(1, it) } + val scaling = settingsDataStoreImpl.getString(SettingsConstants.WALLPAPER_SCALING)?.let { ScalingConstants.valueOf(it) } ?: ScalingConstants.FILL val darken = settingsDataStoreImpl.getBoolean(SettingsConstants.DARKEN) ?: false val darkenPercentage = settingsDataStoreImpl.getInt(SettingsConstants.DARKEN_PERCENTAGE) ?: 100 From 068f78f86cf67befdf2b127aba9087a9ada4a957 Mon Sep 17 00:00:00 2001 From: Anthonyy232 Date: Sun, 2 Jun 2024 16:09:17 -0700 Subject: [PATCH 4/8] Fixed album wallpapers not being refreshed before changing wallpapers Fixed album cover art not being refreshed --- .../wallpaper/presentation/PaperizeApp.kt | 20 +++++++++------ .../presentation/album/AlbumsViewModel.kt | 2 +- .../wallpaper_service/WallpaperService1.kt | 25 ++++++++++++++----- .../wallpaper_service/WallpaperService2.kt | 24 +++++++++++++----- 4 files changed, 51 insertions(+), 20 deletions(-) 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 fc4c887a..2f53ba57 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 @@ -101,9 +101,7 @@ fun PaperizeApp( } } albumsViewModel.onEvent( - AlbumsEvent.DeleteAlbumWithWallpapers( - albumWithWallpapers - ) + AlbumsEvent.DeleteAlbumWithWallpapers(albumWithWallpapers) ) } } @@ -124,10 +122,18 @@ fun PaperizeApp( val wallpapersUri = wallpapers.map { it.wallpaperUri }.toSet() val newSelectedAlbum = SelectedAlbum( album = foundAlbum.album.copy( - homeWallpapersInQueue = selectedAlbum.album.homeWallpapersInQueue.filter { it in wallpapersUri }, - lockWallpapersInQueue = selectedAlbum.album.lockWallpapersInQueue.filter { it in wallpapersUri }, - currentHomeWallpaper = selectedAlbum.album.currentHomeWallpaper, - currentLockWallpaper = selectedAlbum.album.currentLockWallpaper, + homeWallpapersInQueue = if (selectedAlbum.album.homeWallpapersInQueue.firstOrNull() in wallpapersUri) { + val firstElement = selectedAlbum.album.homeWallpapersInQueue.first() + val modifiedWallpapersUri = wallpapersUri.filter { it != firstElement } + listOf(firstElement) + modifiedWallpapersUri.shuffled() + } else { wallpapersUri.shuffled() }, + lockWallpapersInQueue = if (selectedAlbum.album.lockWallpapersInQueue.firstOrNull() in wallpapersUri) { + val firstElement = selectedAlbum.album.lockWallpapersInQueue.first() + val modifiedWallpapersUri = wallpapersUri.filter { it != firstElement } + listOf(firstElement) + modifiedWallpapersUri.shuffled() + } else { wallpapersUri.shuffled() }, + currentHomeWallpaper = if (selectedAlbum.album.currentHomeWallpaper in wallpapersUri) selectedAlbum.album.currentHomeWallpaper else selectedAlbum.album.homeWallpapersInQueue.firstOrNull { it in wallpapersUri }, + currentLockWallpaper = if (selectedAlbum.album.currentLockWallpaper in wallpapersUri) selectedAlbum.album.currentLockWallpaper else selectedAlbum.album.lockWallpapersInQueue.firstOrNull { it in wallpapersUri } ), wallpapers = wallpapers ) 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 1f1badf2..5ff33ece 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 @@ -93,7 +93,7 @@ class AlbumsViewModel @Inject constructor ( repository.deleteWallpaperList(invalidWallpapers) } - // Update folder cover uri and children uri + // Update folder cover uri and wallpapers uri albumWithWallpaper.folders.forEach { folder -> DocumentFileCompat.fromTreeUri(context, folder.folderUri.toUri())?.let { folderDirectory -> if (!folderDirectory.isDirectory()) { diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt index 5f7c58fe..c25e9d1c 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt @@ -41,6 +41,7 @@ import com.lazygeniouz.dfc.file.DocumentFileCompat import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import java.io.IOException @@ -123,6 +124,8 @@ class WallpaperService1: Service() { private fun workerTaskStart(setHomeOrLock: Boolean? = null) { workerHandler.post { CoroutineScope(Dispatchers.IO).launch { + refreshAlbum(this@WallpaperService1) + delay(1000) changeWallpaper(this@WallpaperService1, setHomeOrLock) } stopSelf() @@ -625,14 +628,16 @@ class WallpaperService1: Service() { albumRepository.deleteWallpaperList(invalidWallpapers) } - // Update folder wallpapers + // Update folder cover uri and wallpapers uri albumWithWallpaper.folders.forEach { folder -> DocumentFileCompat.fromTreeUri(context, folder.folderUri.toUri())?.let { folderDirectory -> if (!folderDirectory.isDirectory()) { albumRepository.deleteFolder(folder) } else { val wallpapers = getWallpaperFromFolder(folder.folderUri, context) - albumRepository.updateFolder(folder.copy(wallpapers = wallpapers)) + val folderCoverFile = folder.coverUri?.let { DocumentFile.fromSingleUri(context, it.toUri()) } + val folderCover = folderCoverFile?.takeIf { it.exists() }?.uri?.toString() ?: wallpapers.randomOrNull() + albumRepository.updateFolder(folder.copy(coverUri = folderCover, wallpapers = wallpapers)) } } } @@ -668,10 +673,18 @@ class WallpaperService1: Service() { else { val newSelectedAlbum = SelectedAlbum( album = foundAlbum.album.copy( - homeWallpapersInQueue = wallpapersUri.shuffled(), - lockWallpapersInQueue = wallpapersUri.shuffled(), - currentHomeWallpaper = selectedAlbum.album.currentHomeWallpaper, - currentLockWallpaper = selectedAlbum.album.currentLockWallpaper, + homeWallpapersInQueue = if (selectedAlbum.album.homeWallpapersInQueue.firstOrNull() in wallpapersUri) { + val firstElement = selectedAlbum.album.homeWallpapersInQueue.first() + val modifiedWallpapersUri = wallpapersUri.filter { it != firstElement } + listOf(firstElement) + modifiedWallpapersUri.shuffled() + } else { wallpapersUri.shuffled() }, + lockWallpapersInQueue = if (selectedAlbum.album.lockWallpapersInQueue.firstOrNull() in wallpapersUri) { + val firstElement = selectedAlbum.album.lockWallpapersInQueue.first() + val modifiedWallpapersUri = wallpapersUri.filter { it != firstElement } + listOf(firstElement) + modifiedWallpapersUri.shuffled() + } else { wallpapersUri.shuffled() }, + currentHomeWallpaper = if (selectedAlbum.album.currentHomeWallpaper in wallpapersUri) selectedAlbum.album.currentHomeWallpaper else selectedAlbum.album.homeWallpapersInQueue.firstOrNull { it in wallpapersUri }, + currentLockWallpaper = if (selectedAlbum.album.currentLockWallpaper in wallpapersUri) selectedAlbum.album.currentLockWallpaper else selectedAlbum.album.lockWallpapersInQueue.firstOrNull { it in wallpapersUri } ), wallpapers = wallpapers ) diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt index 9c6ff7f3..22d6ec27 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt @@ -125,6 +125,8 @@ class WallpaperService2: Service() { workerHandler.post { CoroutineScope(Dispatchers.IO).launch { delay(5 * 1000) + refreshAlbum(this@WallpaperService2) + delay(1000) changeWallpaper(this@WallpaperService2, setHomeOrLock) } stopSelf() @@ -630,14 +632,16 @@ class WallpaperService2: Service() { albumRepository.deleteWallpaperList(invalidWallpapers) } - // Update folder wallpapers + // Update folder cover uri and wallpapers uri albumWithWallpaper.folders.forEach { folder -> DocumentFileCompat.fromTreeUri(context, folder.folderUri.toUri())?.let { folderDirectory -> if (!folderDirectory.isDirectory()) { albumRepository.deleteFolder(folder) } else { val wallpapers = getWallpaperFromFolder(folder.folderUri, context) - albumRepository.updateFolder(folder.copy(wallpapers = wallpapers)) + val folderCoverFile = folder.coverUri?.let { DocumentFile.fromSingleUri(context, it.toUri()) } + val folderCover = folderCoverFile?.takeIf { it.exists() }?.uri?.toString() ?: wallpapers.randomOrNull() + albumRepository.updateFolder(folder.copy(coverUri = folderCover, wallpapers = wallpapers)) } } } @@ -673,10 +677,18 @@ class WallpaperService2: Service() { else { val newSelectedAlbum = SelectedAlbum( album = foundAlbum.album.copy( - homeWallpapersInQueue = wallpapersUri.shuffled(), - lockWallpapersInQueue = wallpapersUri.shuffled(), - currentHomeWallpaper = selectedAlbum.album.currentHomeWallpaper, - currentLockWallpaper = selectedAlbum.album.currentLockWallpaper, + homeWallpapersInQueue = if (selectedAlbum.album.homeWallpapersInQueue.firstOrNull() in wallpapersUri) { + val firstElement = selectedAlbum.album.homeWallpapersInQueue.first() + val modifiedWallpapersUri = wallpapersUri.filter { it != firstElement } + listOf(firstElement) + modifiedWallpapersUri.shuffled() + } else { wallpapersUri.shuffled() }, + lockWallpapersInQueue = if (selectedAlbum.album.lockWallpapersInQueue.firstOrNull() in wallpapersUri) { + val firstElement = selectedAlbum.album.lockWallpapersInQueue.first() + val modifiedWallpapersUri = wallpapersUri.filter { it != firstElement } + listOf(firstElement) + modifiedWallpapersUri.shuffled() + } else { wallpapersUri.shuffled() }, + currentHomeWallpaper = if (selectedAlbum.album.currentHomeWallpaper in wallpapersUri) selectedAlbum.album.currentHomeWallpaper else selectedAlbum.album.homeWallpapersInQueue.firstOrNull { it in wallpapersUri }, + currentLockWallpaper = if (selectedAlbum.album.currentLockWallpaper in wallpapersUri) selectedAlbum.album.currentLockWallpaper else selectedAlbum.album.lockWallpapersInQueue.firstOrNull { it in wallpapersUri } ), wallpapers = wallpapers ) From c39527b3b5a27a3ad66ba4f0c4f6d4250139ed4e Mon Sep 17 00:00:00 2001 From: Anthonyy232 Date: Sun, 2 Jun 2024 22:49:03 -0700 Subject: [PATCH 5/8] Reduced delay for delaying album Fixed an issue where DocumentFileCompat would cause a crash on API 26 Fixed an issue where a folder with no name or protected folders like Downloads folder would cause a crash --- .../anthonyla/paperize/core/WallpaperUtil.kt | 38 +++++++++++++++++-- .../wallpaper/presentation/PaperizeApp.kt | 4 +- .../add_album_screen/AddAlbumViewModel.kt | 28 +------------- .../presentation/album/AlbumsViewModel.kt | 29 ++++++++++---- .../AlbumViewScreenViewModel.kt | 27 +------------ .../wallpaper_service/WallpaperService1.kt | 2 +- .../wallpaper_service/WallpaperService2.kt | 4 +- 7 files changed, 65 insertions(+), 67 deletions(-) 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 bd1d2294..e01b5330 100644 --- a/app/src/main/java/com/anthonyla/paperize/core/WallpaperUtil.kt +++ b/app/src/main/java/com/anthonyla/paperize/core/WallpaperUtil.kt @@ -147,12 +147,18 @@ fun blurBitmap(source: Bitmap, percent: Int): Bitmap { * Retrieve wallpaper URIs from a folder directory URI */ fun getWallpaperFromFolder(folderUri: String, context: Context): List { - val folderDocumentFile = DocumentFileCompat.fromTreeUri(context, folderUri.toUri()) - return listFilesRecursive(folderDocumentFile, context) + try { + val folderDocumentFile = DocumentFileCompat.fromTreeUri(context, folderUri.toUri()) + return listFilesRecursive(folderDocumentFile, context) + } catch (e: Exception) { + val folderDocumentFile = DocumentFile.fromTreeUri(context, folderUri.toUri()) + return listFilesRecursive(folderDocumentFile, context) + } + } /** - * Helper function to recursively list files in a directory + * Helper function to recursively list files in a directory for DocumentFileCompat */ fun listFilesRecursive(parent: DocumentFileCompat?, context: Context): List { val files = mutableListOf() @@ -168,6 +174,21 @@ fun listFilesRecursive(parent: DocumentFileCompat?, context: Context): List { + val files = mutableListOf() + parent?.listFiles()?.forEach { file -> + if (file.isDirectory) { + files.addAll(listFilesRecursive(file, context)) + } else { + val allowedExtensions = listOf("jpg", "jpeg", "png", "heif", "webp", "JPG", "JPEG", "PNG", "HEIF", "WEBP") + if ((file.name?.substringAfterLast(".") ?: "") in allowedExtensions) { + files.add(file.uri.toString()) + } + } + } + return files +} /** * Helper function to find the first valid URI from a list of wallpapers @@ -191,3 +212,14 @@ fun findFirstValidUri(context: Context, wallpapers: List, folders: Li return null } +/** + * Get the folder name from the folder URI + */ +fun getFolderNameFromUri(folderUri: String, context: Context): String? { + return try { + DocumentFileCompat.fromTreeUri(context, folderUri.toUri())?.name + } catch (e: Exception) { + DocumentFile.fromTreeUri(context, folderUri.toUri())?.name + } +} + 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 2f53ba57..a84bb968 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 @@ -523,7 +523,7 @@ fun PaperizeApp( }, onShowFolderView = { folderName, wallpapers -> val encodedWallpapers = runBlocking { encodeUri(uri = Gson().toJson(wallpapers)) } - navController.navigate("${NavScreens.FolderView.route}/$folderName/$encodedWallpapers") + navController.navigate("${NavScreens.FolderView.route}/${folderName ?: " "}/$encodedWallpapers") } ) } @@ -635,7 +635,7 @@ fun PaperizeApp( }, onShowFolderView = { folderName, wallpapers -> val encodedWallpapers = runBlocking { encodeUri(uri = Gson().toJson(wallpapers)) } - navController.navigate("${NavScreens.FolderView.route}/$folderName/$encodedWallpapers") + navController.navigate("${NavScreens.FolderView.route}/${folderName ?: " "}/$encodedWallpapers") }, onDeleteAlbum = { navController.navigateUp() 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 e29f2b78..f4b7048b 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 @@ -3,15 +3,15 @@ package com.anthonyla.paperize.feature.wallpaper.presentation.add_album_screen import android.app.Application import android.content.Context -import androidx.core.net.toUri 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.Album 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.lazygeniouz.dfc.file.DocumentFileCompat import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -256,28 +256,4 @@ class AddAlbumViewModel @Inject constructor( } } } - - private fun getWallpaperFromFolder(folderUri: String, context: Context): List { - val folderDocumentFile = DocumentFileCompat.fromTreeUri(context, folderUri.toUri()) - return listFilesRecursive(folderDocumentFile, context) - } - - private fun listFilesRecursive(parent: DocumentFileCompat?, context: Context): List { - val files = mutableListOf() - parent?.listFiles()?.forEach { file -> - if (file.isDirectory()) { - files.addAll(listFilesRecursive(file, context)) - } else { - val allowedExtensions = listOf("jpg", "jpeg", "png", "heif", "webp", "JPG", "JPEG", "PNG", "HEIF", "WEBP") - if (file.extension in allowedExtensions) { - files.add(file.uri.toString()) - } - } - } - return files - } - - private fun getFolderNameFromUri(folderUri: String, context: Context): String? { - return DocumentFileCompat.fromTreeUri(context, folderUri.toUri())?.name - } } \ 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 5ff33ece..30a16ef9 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 @@ -95,14 +95,27 @@ class AlbumsViewModel @Inject constructor ( // Update folder cover uri and wallpapers uri albumWithWallpaper.folders.forEach { folder -> - 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 { DocumentFile.fromSingleUri(context, it.toUri()) } - val folderCover = folderCoverFile?.takeIf { it.exists() }?.uri?.toString() ?: wallpapers.randomOrNull() - repository.updateFolder(folder.copy(coverUri = folderCover, wallpapers = wallpapers)) + 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 { DocumentFile.fromSingleUri(context, it.toUri()) } + val folderCover = folderCoverFile?.takeIf { it.exists() }?.uri?.toString() ?: wallpapers.randomOrNull() + 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) + val folderCoverFile = folder.coverUri?.let { DocumentFile.fromSingleUri(context, it.toUri()) } + val folderCover = folderCoverFile?.takeIf { it.exists() }?.uri?.toString() ?: wallpapers.randomOrNull() + repository.updateFolder(folder.copy(coverUri = folderCover, wallpapers = wallpapers)) + } } } } 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 index 0e605380..55b0319c 100644 --- 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 @@ -3,13 +3,13 @@ package com.anthonyla.paperize.feature.wallpaper.presentation.album_view_screen import android.app.Application import android.content.Context -import androidx.core.net.toUri 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 com.lazygeniouz.dfc.file.DocumentFileCompat import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -215,27 +215,4 @@ class AlbumViewScreenViewModel @Inject constructor( } } - private fun getWallpaperFromFolder(folderUri: String, context: Context): List { - val folderDocumentFile = DocumentFileCompat.fromTreeUri(context, folderUri.toUri()) - return listFilesRecursive(folderDocumentFile, context) - } - - private fun listFilesRecursive(parent: DocumentFileCompat?, context: Context): List { - val files = mutableListOf() - parent?.listFiles()?.forEach { file -> - if (file.isDirectory()) { - files.addAll(listFilesRecursive(file, context)) - } else { - val allowedExtensions = listOf("jpg", "jpeg", "png", "heif", "webp", "JPG", "JPEG", "PNG", "HEIF", "WEBP") - if (file.extension in allowedExtensions) { - files.add(file.uri.toString()) - } - } - } - return files - } - - private fun getFolderNameFromUri(folderUri: String, context: Context): String? { - return DocumentFileCompat.fromTreeUri(context, folderUri.toUri())?.name - } } \ No newline at end of file diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt index c25e9d1c..6b193cf1 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt @@ -125,7 +125,7 @@ class WallpaperService1: Service() { workerHandler.post { CoroutineScope(Dispatchers.IO).launch { refreshAlbum(this@WallpaperService1) - delay(1000) + delay(2000) changeWallpaper(this@WallpaperService1, setHomeOrLock) } stopSelf() diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt index 22d6ec27..d11eb4d1 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt @@ -124,9 +124,9 @@ class WallpaperService2: Service() { private fun workerTaskStart(setHomeOrLock: Boolean? = null) { workerHandler.post { CoroutineScope(Dispatchers.IO).launch { - delay(5 * 1000) + delay(3000) refreshAlbum(this@WallpaperService2) - delay(1000) + delay(2000) changeWallpaper(this@WallpaperService2, setHomeOrLock) } stopSelf() From 322e6907ab03206d971a55b1d31da1d785ad7a03 Mon Sep 17 00:00:00 2001 From: Anthonyy232 Date: Tue, 4 Jun 2024 10:39:38 -0700 Subject: [PATCH 6/8] Updated gradle to 8.8 Fixed ImageDecoder error on API 26 --- .../WallpaperReceiver.kt | 2 - .../wallpaper_service/WallpaperService1.kt | 48 ++++++++++++++----- .../wallpaper_service/WallpaperService2.kt | 47 +++++++++++++----- gradle/wrapper/gradle-wrapper.properties | 4 +- 4 files changed, 73 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperReceiver.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperReceiver.kt index 787f3a25..fcb5e45c 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperReceiver.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_alarmmanager/WallpaperReceiver.kt @@ -22,8 +22,6 @@ class WallpaperReceiver: BroadcastReceiver() { val timeInMinutes2 = intent?.getIntExtra("timeInMinutes2", WALLPAPER_CHANGE_INTERVAL_DEFAULT) ?: WALLPAPER_CHANGE_INTERVAL_DEFAULT val scheduleSeparately = intent?.getBooleanExtra("scheduleSeparately", false) ?: false val type = intent?.getIntExtra("type", Type.BOTH.ordinal) ?: Type.BOTH.ordinal - Log.d("PaperizeWallpaperChanger", "onReceive: timeInMinutes1=$timeInMinutes1, timeInMinutes2=$timeInMinutes2, scheduleSeparately=$scheduleSeparately, type=$type") - val serviceIntent = Intent().apply { putExtra("timeInMinutes1", timeInMinutes1) putExtra("timeInMinutes2", timeInMinutes2) diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt index 6b193cf1..7dd2a374 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService1.kt @@ -538,10 +538,20 @@ class WallpaperService1: Service() { val targetHeight = (targetWidth * aspectRatio).toInt() val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - val source = ImageDecoder.createSource(context.contentResolver, wallpaper) - ImageDecoder.decodeBitmap(source) { decoder, _, _ -> - decoder.setTargetSize(targetWidth, targetHeight) - decoder.isMutableRequired = true + try { + val source = ImageDecoder.createSource(context.contentResolver, wallpaper) + ImageDecoder.decodeBitmap(source) { decoder, _, _ -> + decoder.setTargetSize(targetWidth, targetHeight) + decoder.isMutableRequired = true + } + } catch (e: Exception) { + context.contentResolver.openInputStream(wallpaper)?.use { inputStream -> + val options = BitmapFactory.Options().apply { + inSampleSize = calculateInSampleSize(imageSize, targetWidth, targetHeight) + inMutable = true + } + BitmapFactory.decodeStream(inputStream, null, options) + } } } else { @@ -630,16 +640,30 @@ class WallpaperService1: Service() { // Update folder cover uri and wallpapers uri albumWithWallpaper.folders.forEach { folder -> - DocumentFileCompat.fromTreeUri(context, folder.folderUri.toUri())?.let { folderDirectory -> - if (!folderDirectory.isDirectory()) { - albumRepository.deleteFolder(folder) - } else { - val wallpapers = getWallpaperFromFolder(folder.folderUri, context) - val folderCoverFile = folder.coverUri?.let { DocumentFile.fromSingleUri(context, it.toUri()) } - val folderCover = folderCoverFile?.takeIf { it.exists() }?.uri?.toString() ?: wallpapers.randomOrNull() - albumRepository.updateFolder(folder.copy(coverUri = folderCover, wallpapers = wallpapers)) + try { + DocumentFileCompat.fromTreeUri(context, folder.folderUri.toUri())?.let { folderDirectory -> + if (!folderDirectory.isDirectory()) { + albumRepository.deleteFolder(folder) + } else { + val wallpapers = getWallpaperFromFolder(folder.folderUri, context) + val folderCoverFile = folder.coverUri?.let { DocumentFile.fromSingleUri(context, it.toUri()) } + val folderCover = folderCoverFile?.takeIf { it.exists() }?.uri?.toString() ?: wallpapers.randomOrNull() + albumRepository.updateFolder(folder.copy(coverUri = folderCover, wallpapers = wallpapers)) + } + } + } catch (e: Exception) { + DocumentFile.fromTreeUri(context, folder.folderUri.toUri())?.let { folderDirectory -> + if (!folderDirectory.isDirectory) { + albumRepository.deleteFolder(folder) + } else { + val wallpapers = getWallpaperFromFolder(folder.folderUri, context) + val folderCoverFile = folder.coverUri?.let { DocumentFile.fromSingleUri(context, it.toUri()) } + val folderCover = folderCoverFile?.takeIf { it.exists() }?.uri?.toString() ?: wallpapers.randomOrNull() + albumRepository.updateFolder(folder.copy(coverUri = folderCover, wallpapers = wallpapers)) + } } } + } // Delete empty albums diff --git a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt index d11eb4d1..52300c2b 100644 --- a/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt +++ b/app/src/main/java/com/anthonyla/paperize/feature/wallpaper/wallpaper_service/WallpaperService2.kt @@ -542,10 +542,20 @@ class WallpaperService2: Service() { val targetHeight = (targetWidth * aspectRatio).toInt() val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - val source = ImageDecoder.createSource(context.contentResolver, wallpaper) - ImageDecoder.decodeBitmap(source) { decoder, _, _ -> - decoder.setTargetSize(targetWidth, targetHeight) - decoder.isMutableRequired = true + try { + val source = ImageDecoder.createSource(context.contentResolver, wallpaper) + ImageDecoder.decodeBitmap(source) { decoder, _, _ -> + decoder.setTargetSize(targetWidth, targetHeight) + decoder.isMutableRequired = true + } + } catch (e: Exception) { + context.contentResolver.openInputStream(wallpaper)?.use { inputStream -> + val options = BitmapFactory.Options().apply { + inSampleSize = calculateInSampleSize(imageSize, targetWidth, targetHeight) + inMutable = true + } + BitmapFactory.decodeStream(inputStream, null, options) + } } } else { @@ -634,14 +644,27 @@ class WallpaperService2: Service() { // Update folder cover uri and wallpapers uri albumWithWallpaper.folders.forEach { folder -> - DocumentFileCompat.fromTreeUri(context, folder.folderUri.toUri())?.let { folderDirectory -> - if (!folderDirectory.isDirectory()) { - albumRepository.deleteFolder(folder) - } else { - val wallpapers = getWallpaperFromFolder(folder.folderUri, context) - val folderCoverFile = folder.coverUri?.let { DocumentFile.fromSingleUri(context, it.toUri()) } - val folderCover = folderCoverFile?.takeIf { it.exists() }?.uri?.toString() ?: wallpapers.randomOrNull() - albumRepository.updateFolder(folder.copy(coverUri = folderCover, wallpapers = wallpapers)) + try { + DocumentFileCompat.fromTreeUri(context, folder.folderUri.toUri())?.let { folderDirectory -> + if (!folderDirectory.isDirectory()) { + albumRepository.deleteFolder(folder) + } else { + val wallpapers = getWallpaperFromFolder(folder.folderUri, context) + val folderCoverFile = folder.coverUri?.let { DocumentFile.fromSingleUri(context, it.toUri()) } + val folderCover = folderCoverFile?.takeIf { it.exists() }?.uri?.toString() ?: wallpapers.randomOrNull() + albumRepository.updateFolder(folder.copy(coverUri = folderCover, wallpapers = wallpapers)) + } + } + } catch (e: Exception) { + DocumentFile.fromTreeUri(context, folder.folderUri.toUri())?.let { folderDirectory -> + if (!folderDirectory.isDirectory) { + albumRepository.deleteFolder(folder) + } else { + val wallpapers = getWallpaperFromFolder(folder.folderUri, context) + val folderCoverFile = folder.coverUri?.let { DocumentFile.fromSingleUri(context, it.toUri()) } + val folderCover = folderCoverFile?.takeIf { it.exists() }?.uri?.toString() ?: wallpapers.randomOrNull() + albumRepository.updateFolder(folder.copy(coverUri = folderCover, wallpapers = wallpapers)) + } } } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index acd7ac5a..d5984f62 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Apr 30 13:31:42 PDT 2024 +#Mon Jun 03 14:51:19 PDT 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 219a4cdb524c6926372c9eed0c7f1e69eb381ae9 Mon Sep 17 00:00:00 2001 From: Anthonyy232 Date: Tue, 4 Jun 2024 10:51:42 -0700 Subject: [PATCH 7/8] Changed gradle to include debug symbols automatically --- app/build.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 08e880dc..d65dc7f9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -25,6 +25,9 @@ android { vectorDrawables { useSupportLibrary = true } + ndk { + debugSymbolLevel = "FULL" + } } From 1ff253957437cf6d1acb980771b1ab9e1838d50e Mon Sep 17 00:00:00 2001 From: Anthonyy232 Date: Tue, 4 Jun 2024 10:54:22 -0700 Subject: [PATCH 8/8] Updated version code --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d65dc7f9..fcfca2d1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -18,7 +18,7 @@ android { applicationId = "com.anthonyla.paperize" minSdk = 26 targetSdk = 34 - versionCode = 16 + versionCode = 17 versionName = "1.5.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"