From ab1df75db99282e1784b9e359e7aa4862dd88889 Mon Sep 17 00:00:00 2001 From: Kieron Quinn Date: Sat, 3 Feb 2024 22:05:28 +0000 Subject: [PATCH] - Fixed screen on recognition not working on some devices - Fixed externally triggered recognitions not showing on the lock screen - Updated target SDK to Android 14 - Added support for displaying on the flip display on Samsung devices - Bug fixes --- app/build.gradle | 43 +++++++------- app/release/output-metadata.json | 4 +- app/src/main/AndroidManifest.xml | 13 ++-- .../EncryptedSettingsRepository.kt | 59 ++----------------- .../repositories/ExternalAccessRepository.kt | 16 ++--- .../repositories/RemoteSettingsRepository.kt | 26 +++++++- .../AmbientMusicModForegroundService.kt | 47 ++++++++++++--- .../ambientmusicmod/service/ShizukuService.kt | 6 +- .../ui/base/BaseContainerFragment.kt | 5 +- .../screens/nowplaying/NowPlayingFragment.kt | 10 +++- .../SettingsAdvancedExternalAccessFragment.kt | 9 --- ...SettingsAdvancedExternalAccessViewModel.kt | 16 +---- .../SettingsExtraCountryPickerFragment.kt | 2 +- .../setup/datausage/SetupDataUsageFragment.kt | 4 +- .../permissions/SetupPermissionsFragment.kt | 4 +- .../utils/alarm/AlarmTimeout.kt | 19 ++++-- .../utils/extensions/Extensions+Context.kt | 14 ++++- .../Extensions+EncryptedSharedPreferences.kt | 48 +++++++++++++++ .../utils/extensions/Extensions+Inset.kt | 13 ++-- .../utils/extensions/Extensions+Keyguard.kt | 2 +- .../utils/extensions/Extensions+Service.kt | 23 ++++++++ .../utils/extensions/Extensions+Wallpaper.kt | 2 +- .../work/PeriodicBackupWorker.kt | 26 ++++++-- app/src/main/res/raw/faq.md | 4 +- app/src/main/res/values/strings.xml | 1 - app/src/main/res/xml/samsung_widget.xml | 2 + astrea/build.gradle | 1 + astrea/src/main/AndroidManifest.xml | 3 +- build.gradle | 15 ++--- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.properties | 4 +- ondemandoverlay/build.gradle | 1 + ondemandoverlay/src/main/AndroidManifest.xml | 1 - systemstubs/build.gradle | 4 ++ systemstubs/src/main/AndroidManifest.xml | 3 +- 35 files changed, 281 insertions(+), 173 deletions(-) create mode 100644 app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+EncryptedSharedPreferences.kt create mode 100644 app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Service.kt create mode 100644 app/src/main/res/xml/samsung_widget.xml diff --git a/app/build.gradle b/app/build.gradle index 3e641e4..fb80fcf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,15 +7,16 @@ plugins { id 'dagger.hilt.android.plugin' id 'com.google.protobuf' id 'dev.rikka.tools.refine' + id 'com.google.devtools.ksp' } apply plugin: 'com.google.android.gms.oss-licenses-plugin' -String DEFAULT_MANIFEST = "248:https://storage.googleapis.com/music-iq-db/updatable_ytm_db/20230507-030029/manifest.json" -String DEFAULT_MANIFEST_V3 = "3050:https://storage.googleapis.com/music-iq-db/updatable_db_v3/20230507-030029/manifest.json" +String DEFAULT_MANIFEST = "266:https://storage.googleapis.com/music-iq-db/updatable_ytm_db/20240128-030108/manifest.json" +String DEFAULT_MANIFEST_V3 = "3073:https://storage.googleapis.com/music-iq-db/updatable_db_v3/20240128-030108/manifest.json" -def tagName = '2.3.1' -def version = 231 +def tagName = '2.3.2' +def version = 232 def getKeystoreProperties() { def properties = new Properties() @@ -41,7 +42,7 @@ android { defaultConfig { applicationId "com.kieronquinn.app.ambientmusicmod" minSdk 28 - targetSdk 33 + targetSdk 34 versionCode version versionName tagName @@ -86,7 +87,9 @@ android { } buildFeatures { viewBinding true + aidl true } + namespace 'com.kieronquinn.app.ambientmusicmod' } protobuf { @@ -111,31 +114,31 @@ protobuf { dependencies { //AndroidX - implementation 'androidx.core:core-ktx:1.10.1' + implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.core:core-splashscreen:1.0.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version" - implementation "androidx.work:work-runtime-ktx:2.8.1" + implementation "androidx.work:work-runtime-ktx:2.9.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" - implementation "androidx.lifecycle:lifecycle-service:2.6.1" - implementation "androidx.security:security-crypto:1.0.0" + implementation "androidx.lifecycle:lifecycle-service:2.7.0" + implementation "androidx.security:security-crypto:1.1.0-alpha06" //Material UI - implementation 'com.google.android.material:material:1.9.0' + implementation 'com.google.android.material:material:1.11.0' //MonetCompat for colours on < S implementation 'com.github.KieronQuinn:MonetCompat:0.4.1' //Dependency Injection - implementation "io.insert-koin:koin-android:3.4.0" + implementation "io.insert-koin:koin-android:3.5.0" //Protobuf is used in reading data from ASI - implementation "com.google.protobuf:protobuf-javalite:3.21.9" + implementation "com.google.protobuf:protobuf-javalite:3.25.2" //Lottie for animations - implementation 'com.airbnb.android:lottie:5.2.0' + implementation 'com.airbnb.android:lottie:6.3.0' //Used for chip layouts implementation 'com.google.android.flexbox:flexbox:3.0.0' @@ -144,7 +147,7 @@ dependencies { implementation 'com.github.alxrm:audiowave-progressbar:0.9.2' implementation 'com.google.guava:guava:31.1-android' - implementation 'com.google.code.gson:gson:2.9.1' + implementation 'com.google.code.gson:gson:2.10.1' //Update & APK downloading implementation 'com.squareup.retrofit2:retrofit:2.9.0' @@ -174,10 +177,10 @@ dependencies { implementation "dev.rikka.tools.refine:runtime:$refine_version" //Dagger + Hilt for injection in Astrea - implementation "com.google.dagger:hilt-android:2.38.1" - implementation 'com.google.dagger:dagger:2.41' - kapt 'com.google.dagger:dagger-compiler:2.41' - kapt "com.google.dagger:hilt-compiler:2.41" + implementation "com.google.dagger:hilt-android:2.50" + implementation 'com.google.dagger:dagger:2.50' + kapt 'com.google.dagger:dagger-compiler:2.50' + kapt "com.google.dagger:hilt-compiler:2.50" //OSS libraries activity implementation 'com.google.android.gms:play-services-oss-licenses:17.0.1' @@ -192,9 +195,9 @@ dependencies { implementation 'com.github.duanhong169:colorpicker:1.1.6' //Room for local song list cache - def room_version = "2.5.1" + def room_version = "2.6.1" implementation "androidx.room:room-runtime:$room_version" - kapt "androidx.room:room-compiler:$room_version" + ksp "androidx.room:room-compiler:$room_version" testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index 49d765e..ed8ee12 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -11,8 +11,8 @@ "type": "SINGLE", "filters": [], "attributes": [], - "versionCode": 230, - "versionName": "2.3", + "versionCode": 232, + "versionName": "2.3.2", "outputFile": "app-release.apk" } ], diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 704450a..a3139a2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> + @@ -79,7 +79,8 @@ + android:exported="true" + tools:ignore="ExportedReceiver"> @@ -89,7 +90,8 @@ - + + diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/repositories/EncryptedSettingsRepository.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/repositories/EncryptedSettingsRepository.kt index 1e20c12..33a06f3 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/repositories/EncryptedSettingsRepository.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/repositories/EncryptedSettingsRepository.kt @@ -1,24 +1,17 @@ package com.kieronquinn.app.ambientmusicmod.repositories import android.content.Context -import android.content.SharedPreferences -import androidx.security.crypto.EncryptedSharedPreferences -import androidx.security.crypto.MasterKeys import com.kieronquinn.app.ambientmusicmod.BuildConfig import com.kieronquinn.app.ambientmusicmod.repositories.BaseSettingsRepository.AmbientMusicModSetting +import com.kieronquinn.app.ambientmusicmod.utils.extensions.createEncryptedSharedPrefDestructively import com.kieronquinn.app.ambientmusicmod.utils.extensions.randomSecureString -import java.security.KeyStore interface EncryptedSettingsRepository { - - val encryptionAvailable: Boolean - val externalAccessEnabled: AmbientMusicModSetting val externalAccessRequireToken: AmbientMusicModSetting val externalAccessToken: AmbientMusicModSetting val externalAccessToggleEnabled: AmbientMusicModSetting val externalAccessRecognitionEnabled: AmbientMusicModSetting - } class EncryptedSettingsRepositoryImpl( @@ -42,56 +35,14 @@ class EncryptedSettingsRepositoryImpl( private const val EXTERNAL_ACCESS_RECOGNITION_ENABLED = "external_access_recognition_enabled" private const val DEFAULT_ACCESS_EXTERNAL_RECOGNITION_ENABLED = true - - private fun tryLoadSharedPreferences(context: Context): SharedPreferences? { - //Regular load, should work 99% of the time - getSharedPreferences(context)?.let { - return it - } - //If failed, delete the key and start again - deleteMasterKeyEntry() - //If it still fails, nothing we can do - return getSharedPreferences(context) - } - - private fun getSharedPreferences(context: Context): SharedPreferences? { - return try { - val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC - val mainKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec) - EncryptedSharedPreferences.create( - "${BuildConfig.APPLICATION_ID}_encrypted_prefs", - mainKeyAlias, - context, - EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, - EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM - ) - }catch (e: Exception) { - //Failed to load shared prefs - null - } - } - - private fun deleteMasterKeyEntry() { - try { - KeyStore.getInstance("AndroidKeyStore").apply { - load(null) - deleteEntry("_androidx_security_master_key_") - } - }catch (e: Exception){ - //Failed to delete key - } - } } - private val encryptedSharedPreferences by lazy { - tryLoadSharedPreferences(context) + override val sharedPreferences by lazy { + context.createEncryptedSharedPrefDestructively( + "${BuildConfig.APPLICATION_ID}_encrypted_prefs" + ) } - override val sharedPreferences - get() = encryptedSharedPreferences ?: throw RuntimeException("Encrypted prefs failed to load") - - override val encryptionAvailable = encryptedSharedPreferences != null - override val externalAccessEnabled = boolean(EXTERNAL_ACCESS_ENABLED, DEFAULT_EXTERNAL_ACCESS_ENABLED) diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/repositories/ExternalAccessRepository.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/repositories/ExternalAccessRepository.kt index 3de7da0..c4b6e3c 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/repositories/ExternalAccessRepository.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/repositories/ExternalAccessRepository.kt @@ -1,11 +1,12 @@ package com.kieronquinn.app.ambientmusicmod.repositories import android.content.Intent +import com.kieronquinn.app.ambientmusicmod.repositories.RecognitionRepository.RecognitionState import com.kieronquinn.app.ambientmusicmod.repositories.RemoteSettingsRepository.SettingsState +import com.kieronquinn.app.ambientmusicmod.service.AmbientMusicModForegroundService import com.kieronquinn.app.ambientmusicmod.utils.extensions.firstNotNull import com.kieronquinn.app.pixelambientmusic.model.SettingsStateChange import kotlinx.coroutines.MainScope -import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout @@ -64,7 +65,11 @@ class ExternalAccessRepositoryImpl( recognitionRepository.requestOnDemandRecognition() }else{ recognitionRepository.requestRecognition() - }.launchIn(scope) + }.collect { + if(it is RecognitionState.Recognised){ + AmbientMusicModForegroundService.sendManualRecognition(it) + } + } } } @@ -85,21 +90,18 @@ class ExternalAccessRepositoryImpl( } private suspend fun verifyTokenIfRequired(intent: Intent): Boolean { - if(!encryptedSettings.encryptionAvailable) return false if(!encryptedSettings.externalAccessRequireToken.get()) return true val token = intent.getStringExtra(EXTRA_TOKEN) ?: return false return encryptedSettings.externalAccessToken.get() == token } private suspend fun toggleEnabled(): Boolean { - return encryptedSettings.encryptionAvailable - && encryptedSettings.externalAccessEnabled.get() + return encryptedSettings.externalAccessEnabled.get() && encryptedSettings.externalAccessToggleEnabled.get() } private suspend fun recognitionEnabled(): Boolean { - return encryptedSettings.encryptionAvailable - && encryptedSettings.externalAccessEnabled.get() + return encryptedSettings.externalAccessEnabled.get() && encryptedSettings.externalAccessRecognitionEnabled.get() } diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/repositories/RemoteSettingsRepository.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/repositories/RemoteSettingsRepository.kt index 3a14666..2997a46 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/repositories/RemoteSettingsRepository.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/repositories/RemoteSettingsRepository.kt @@ -20,13 +20,33 @@ import com.kieronquinn.app.ambientmusicmod.PACKAGE_NAME_PAM import com.kieronquinn.app.ambientmusicmod.model.settings.BannerMessage import com.kieronquinn.app.ambientmusicmod.model.settings.toLocalBannerMessage import com.kieronquinn.app.ambientmusicmod.repositories.RemoteSettingsRepository.* -import com.kieronquinn.app.ambientmusicmod.utils.extensions.* +import com.kieronquinn.app.ambientmusicmod.utils.extensions.contentResolverAsTFlow +import com.kieronquinn.app.ambientmusicmod.utils.extensions.getNetworkCapabilities +import com.kieronquinn.app.ambientmusicmod.utils.extensions.getSplits +import com.kieronquinn.app.ambientmusicmod.utils.extensions.isArmv7 +import com.kieronquinn.app.ambientmusicmod.utils.extensions.isOnDemandConfigValueSet +import com.kieronquinn.app.ambientmusicmod.utils.extensions.isPermissionGranted +import com.kieronquinn.app.ambientmusicmod.utils.extensions.isX86_64 +import com.kieronquinn.app.ambientmusicmod.utils.extensions.map +import com.kieronquinn.app.ambientmusicmod.utils.extensions.onPackageChanged +import com.kieronquinn.app.ambientmusicmod.utils.extensions.registerReceiverCompat +import com.kieronquinn.app.ambientmusicmod.utils.extensions.safeQuery import com.kieronquinn.app.pixelambientmusic.model.LastRecognisedSong import com.kieronquinn.app.pixelambientmusic.model.SettingsStateChange import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext import com.kieronquinn.app.pixelambientmusic.model.SettingsState as RemoteSettingsState @@ -162,7 +182,7 @@ class RemoteSettingsRepositoryImpl( } } trySend(isEnabled()) - context.registerReceiver( + context.registerReceiverCompat( receiver, IntentFilter(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED) ) awaitClose { diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/service/AmbientMusicModForegroundService.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/service/AmbientMusicModForegroundService.kt index 40ef3f5..83eabeb 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/service/AmbientMusicModForegroundService.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/service/AmbientMusicModForegroundService.kt @@ -24,23 +24,52 @@ import com.kieronquinn.app.ambientmusicmod.model.lockscreenoverlay.OverlayState import com.kieronquinn.app.ambientmusicmod.model.lockscreenoverlay.stateEquals import com.kieronquinn.app.ambientmusicmod.model.recognition.Player import com.kieronquinn.app.ambientmusicmod.model.settings.BannerMessage -import com.kieronquinn.app.ambientmusicmod.repositories.* +import com.kieronquinn.app.ambientmusicmod.repositories.AccessibilityRepository +import com.kieronquinn.app.ambientmusicmod.repositories.BedtimeRepository +import com.kieronquinn.app.ambientmusicmod.repositories.DeviceConfigRepository +import com.kieronquinn.app.ambientmusicmod.repositories.RecognitionRepository import com.kieronquinn.app.ambientmusicmod.repositories.RecognitionRepository.RecognitionState import com.kieronquinn.app.ambientmusicmod.repositories.RecognitionRepository.RecognitionState.ErrorReason +import com.kieronquinn.app.ambientmusicmod.repositories.RemoteSettingsRepository import com.kieronquinn.app.ambientmusicmod.repositories.RemoteSettingsRepository.SettingsState +import com.kieronquinn.app.ambientmusicmod.repositories.SettingsRepository import com.kieronquinn.app.ambientmusicmod.repositories.SettingsRepository.LockscreenOnTrackClicked import com.kieronquinn.app.ambientmusicmod.repositories.SettingsRepository.RecognitionPeriod +import com.kieronquinn.app.ambientmusicmod.repositories.ShizukuServiceRepository +import com.kieronquinn.app.ambientmusicmod.repositories.WidgetRepository import com.kieronquinn.app.ambientmusicmod.ui.activities.MainActivity import com.kieronquinn.app.ambientmusicmod.utils.alarm.AlarmTimeout import com.kieronquinn.app.ambientmusicmod.utils.alarm.AlarmTimeout.Companion.MODE_RESCHEDULE_IF_SCHEDULED -import com.kieronquinn.app.ambientmusicmod.utils.extensions.* +import com.kieronquinn.app.ambientmusicmod.utils.extensions.applySecurity +import com.kieronquinn.app.ambientmusicmod.utils.extensions.autoClearAfterBy +import com.kieronquinn.app.ambientmusicmod.utils.extensions.batterySaverEnabled +import com.kieronquinn.app.ambientmusicmod.utils.extensions.broadcastReceiverAsFlow +import com.kieronquinn.app.ambientmusicmod.utils.extensions.dismissKeyguard +import com.kieronquinn.app.ambientmusicmod.utils.extensions.ellipsizeToSize +import com.kieronquinn.app.ambientmusicmod.utils.extensions.firstNotNull +import com.kieronquinn.app.ambientmusicmod.utils.extensions.startForeground +import com.kieronquinn.app.ambientmusicmod.utils.extensions.verifySecurity +import com.kieronquinn.app.ambientmusicmod.utils.extensions.whenCreated import com.kieronquinn.app.pixelambientmusic.model.RecognitionFailure import com.kieronquinn.app.pixelambientmusic.model.RecognitionFailureReason import com.kieronquinn.app.pixelambientmusic.model.RecognitionMetadata import com.kieronquinn.app.pixelambientmusic.model.RecognitionSource import kotlinx.coroutines.Job import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.drop +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.stateIn import org.koin.android.ext.android.inject import java.time.Duration import java.time.LocalDateTime @@ -95,6 +124,9 @@ class AmbientMusicModForegroundService: LifecycleService() { } } + private val loggingEnabled = deviceConfig.enableLogging.asFlow() + .stateIn(lifecycleScope, SharingStarted.Eagerly, deviceConfig.enableLogging.getSync()) + private val delayTime = combine( settings.recognitionPeriod.asFlow(), settings.recognitionBuffer.asFlow() @@ -266,9 +298,6 @@ class AmbientMusicModForegroundService: LifecycleService() { private val bufferTime = settings.recognitionBuffer.asFlow() .stateIn(lifecycleScope, SharingStarted.Eagerly, null) - private val loggingEnabled = deviceConfig.enableLogging.asFlow() - .stateIn(lifecycleScope, SharingStarted.Eagerly, deviceConfig.enableLogging.getSync()) - private val minuteTicker by lazy { AlarmTimeout(alarmManager, alarmListener, ALARM_ID, handler) } @@ -311,7 +340,7 @@ class AmbientMusicModForegroundService: LifecycleService() { override fun onCreate() { super.onCreate() MESSAGE_HANDLER = messageHandler - startForeground(NotificationId.FOREGROUND_SERVICE.ordinal, showNotification()) + startForeground(NotificationId.FOREGROUND_SERVICE, showNotification()) setupRecogniser() setupOverlay() setupScreenOn() @@ -389,7 +418,9 @@ class AmbientMusicModForegroundService: LifecycleService() { private fun setupScreenOn() = whenCreated { screenOnTrigger.collect { - recognitionState.emit(null) + recognition.requestRecognition().collect { + recognitionState.emit(it) + } } } diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/service/ShizukuService.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/service/ShizukuService.kt index e100a5e..faec4cc 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/service/ShizukuService.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/service/ShizukuService.kt @@ -79,6 +79,10 @@ class ShizukuService: IShellProxy.Stub() { }else null } + private val defaultAudioFormat by lazy { + AudioFormat.Builder().build() + } + private val musicRecognitionManager by lazy { context.getSystemService("music_recognition") as MusicRecognitionManager } @@ -156,7 +160,7 @@ class ShizukuService: IShellProxy.Stub() { } override fun AudioRecord_getFormat(): AudioFormat { - return audioRecord.format + return _audioRecord?.format ?: defaultAudioFormat } override fun AudioRecord_getBufferSizeInFrames(): Int { diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/base/BaseContainerFragment.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/base/BaseContainerFragment.kt index cd0e079..c80df07 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/base/BaseContainerFragment.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/base/BaseContainerFragment.kt @@ -20,7 +20,6 @@ import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentContainerView -import androidx.lifecycle.lifecycleScope import androidx.navigation.NavDestination import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.NavigationUI @@ -34,8 +33,6 @@ import com.kieronquinn.app.ambientmusicmod.components.navigation.setupWithNaviga import com.kieronquinn.app.ambientmusicmod.utils.extensions.* import com.kieronquinn.app.ambientmusicmod.utils.monetcompat.MonetElevationOverlayProvider import com.kieronquinn.monetcompat.extensions.toArgb -import com.kieronquinn.monetcompat.extensions.views.applyMonet -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch abstract class BaseContainerFragment(inflate: (LayoutInflater, ViewGroup?, Boolean) -> V): BoundFragment(inflate) { @@ -88,7 +85,7 @@ abstract class BaseContainerFragment(inflate: (LayoutInflater, V } private fun BottomNavigationView.setupBottomNavigation() { - val legacyWorkaround = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + val legacyWorkaround = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { context.getLegacyWorkaroundNavBarHeight() } else 0 onApplyInsets { view, insets -> diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/nowplaying/NowPlayingFragment.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/nowplaying/NowPlayingFragment.kt index 146ade2..12d7a48 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/nowplaying/NowPlayingFragment.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/nowplaying/NowPlayingFragment.kt @@ -25,7 +25,13 @@ import com.kieronquinn.app.ambientmusicmod.ui.base.ProvidesOverflow import com.kieronquinn.app.ambientmusicmod.ui.base.Root import com.kieronquinn.app.ambientmusicmod.ui.screens.nowplaying.NowPlayingViewModel.NowPlayingSettingsItem import com.kieronquinn.app.ambientmusicmod.ui.screens.nowplaying.NowPlayingViewModel.State -import com.kieronquinn.app.ambientmusicmod.utils.extensions.* +import com.kieronquinn.app.ambientmusicmod.utils.extensions.applyBottomNavigationInset +import com.kieronquinn.app.ambientmusicmod.utils.extensions.collapse +import com.kieronquinn.app.ambientmusicmod.utils.extensions.isDarkMode +import com.kieronquinn.app.ambientmusicmod.utils.extensions.onApplyInsets +import com.kieronquinn.app.ambientmusicmod.utils.extensions.onClicked +import com.kieronquinn.app.ambientmusicmod.utils.extensions.shouldShrinkFab +import com.kieronquinn.app.ambientmusicmod.utils.extensions.whenResumed import com.kieronquinn.monetcompat.extensions.views.applyMonet import org.koin.androidx.viewmodel.ext.android.viewModel @@ -76,7 +82,7 @@ class NowPlayingFragment: BoundFragment(FragmentNowPl private fun setupFab() = with(binding.fabNowplayingRecognise){ backgroundTintList = ColorStateList.valueOf(monet.getPrimaryColor(context)) - val legacyWorkaround = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + val legacyWorkaround = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { context.getLegacyWorkaroundNavBarHeight() } else 0 onApplyInsets { _, insets -> diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/settings/advanced/externalaccess/SettingsAdvancedExternalAccessFragment.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/settings/advanced/externalaccess/SettingsAdvancedExternalAccessFragment.kt index 4fe6ed7..6ab097a 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/settings/advanced/externalaccess/SettingsAdvancedExternalAccessFragment.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/settings/advanced/externalaccess/SettingsAdvancedExternalAccessFragment.kt @@ -2,7 +2,6 @@ package com.kieronquinn.app.ambientmusicmod.ui.screens.settings.advanced.externa import android.os.Bundle import android.view.View -import android.widget.Toast import androidx.core.view.isVisible import com.kieronquinn.app.ambientmusicmod.R import com.kieronquinn.app.ambientmusicmod.model.settings.BaseSettingsItem @@ -47,14 +46,6 @@ class SettingsAdvancedExternalAccessFragment: BaseSettingsFragment(), BackAvaila binding.settingsBaseRecyclerView.isVisible = true adapter.update(state.loadItems(), binding.settingsBaseRecyclerView) } - is State.FailedToLoadSettings -> { - Toast.makeText( - requireContext(), - R.string.settings_external_access_encryption_failed, - Toast.LENGTH_LONG - ).show() - viewModel.navigateBack() - } } } diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/settings/advanced/externalaccess/SettingsAdvancedExternalAccessViewModel.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/settings/advanced/externalaccess/SettingsAdvancedExternalAccessViewModel.kt index 2b7e067..c85a6bc 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/settings/advanced/externalaccess/SettingsAdvancedExternalAccessViewModel.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/settings/advanced/externalaccess/SettingsAdvancedExternalAccessViewModel.kt @@ -18,8 +18,6 @@ import com.kieronquinn.app.ambientmusicmod.utils.extensions.randomSecureString import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -46,8 +44,7 @@ abstract class SettingsAdvancedExternalAccessViewModel: ViewModel() { } sealed class State { - object Loading: State() - object FailedToLoadSettings: State() + data object Loading: State() data class Loaded( val enabled: Boolean, val toggleEnabled: Boolean, @@ -70,7 +67,7 @@ class SettingsAdvancedExternalAccessViewModelImpl( private val requireTokenEnabled = settings.externalAccessRequireToken private val accessToken = settings.externalAccessToken - private val encryptedSettings = combine( + override val state = combine( enabled.asFlow(), toggleEnabled.asFlow(), recognitionEnabled.asFlow(), @@ -84,15 +81,6 @@ class SettingsAdvancedExternalAccessViewModelImpl( requireToken, token ) - } - - override val state = flowOf(settings.encryptionAvailable).flatMapLatest { - if(it) { - encryptedSettings - } else { - //Failed to load encrypted settings so don't hit the combine and instead show error - flowOf(State.FailedToLoadSettings) - } }.stateIn(viewModelScope, SharingStarted.Eagerly, State.Loading) override fun onEnabledChanged(enabled: Boolean) { diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/settings/extracountrypicker/SettingsExtraCountryPickerFragment.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/settings/extracountrypicker/SettingsExtraCountryPickerFragment.kt index d585e55..d508dc0 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/settings/extracountrypicker/SettingsExtraCountryPickerFragment.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/settings/extracountrypicker/SettingsExtraCountryPickerFragment.kt @@ -60,7 +60,7 @@ class SettingsExtraCountryPickerFragment: BoundFragment diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/setup/datausage/SetupDataUsageFragment.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/setup/datausage/SetupDataUsageFragment.kt index 126fb10..6bf654c 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/setup/datausage/SetupDataUsageFragment.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/setup/datausage/SetupDataUsageFragment.kt @@ -7,7 +7,6 @@ import android.view.View import androidx.core.view.WindowInsetsCompat import androidx.core.view.isVisible import androidx.core.view.updatePadding -import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.kieronquinn.app.ambientmusicmod.R import com.kieronquinn.app.ambientmusicmod.databinding.FragmentSetupDataUsageBinding @@ -24,7 +23,6 @@ import com.kieronquinn.app.ambientmusicmod.utils.extensions.onClicked import com.kieronquinn.app.ambientmusicmod.utils.extensions.whenResumed import com.kieronquinn.monetcompat.extensions.views.applyMonet import com.kieronquinn.monetcompat.extensions.views.overrideRippleColor -import kotlinx.coroutines.flow.collect import org.koin.androidx.viewmodel.ext.android.viewModel class SetupDataUsageFragment: BoundFragment(FragmentSetupDataUsageBinding::inflate), BackAvailable, ProvidesBack { @@ -114,7 +112,7 @@ class SetupDataUsageFragment: BoundFragment(Fragm private fun setupInsets() { val standardPadding = resources.getDimension(R.dimen.margin_16).toInt() - val legacyWorkaround = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + val legacyWorkaround = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { requireContext().getLegacyWorkaroundNavBarHeight() } else 0 binding.setupDataUsageControls.onApplyInsets { view, insets -> diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/setup/permissions/SetupPermissionsFragment.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/setup/permissions/SetupPermissionsFragment.kt index bb48ade..ff53963 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/setup/permissions/SetupPermissionsFragment.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/ui/screens/setup/permissions/SetupPermissionsFragment.kt @@ -7,7 +7,6 @@ import android.view.View import androidx.core.view.WindowInsetsCompat import androidx.core.view.isVisible import androidx.core.view.updatePadding -import androidx.lifecycle.lifecycleScope import com.kieronquinn.app.ambientmusicmod.R import com.kieronquinn.app.ambientmusicmod.databinding.FragmentSetupPermissionsBinding import com.kieronquinn.app.ambientmusicmod.ui.base.BackAvailable @@ -20,7 +19,6 @@ import com.kieronquinn.app.ambientmusicmod.utils.extensions.onClicked import com.kieronquinn.app.ambientmusicmod.utils.extensions.whenResumed import com.kieronquinn.monetcompat.extensions.views.applyMonet import com.kieronquinn.monetcompat.extensions.views.overrideRippleColor -import kotlinx.coroutines.flow.collect import org.koin.androidx.viewmodel.ext.android.viewModel class SetupPermissionsFragment: BoundFragment(FragmentSetupPermissionsBinding::inflate), BackAvailable { @@ -51,7 +49,7 @@ class SetupPermissionsFragment: BoundFragment(F private fun setupInsets() { val standardPadding = resources.getDimension(R.dimen.margin_16).toInt() - val legacyWorkaround = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + val legacyWorkaround = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { requireContext().getLegacyWorkaroundNavBarHeight() } else 0 binding.setupPermissionsControls.onApplyInsets { view, insets -> diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/alarm/AlarmTimeout.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/alarm/AlarmTimeout.kt index 5581582..b596745 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/alarm/AlarmTimeout.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/alarm/AlarmTimeout.kt @@ -26,8 +26,7 @@ import android.os.SystemClock */ class AlarmTimeout(private val mAlarmManager: AlarmManager, private val mListener: OnAlarmListener, private val mTag: String, private val mHandler: Handler) : OnAlarmListener { - var isScheduled = false - private set + private var isScheduled = false /** * Schedules an alarm in `timeout` milliseconds in the future. @@ -48,10 +47,18 @@ class AlarmTimeout(private val mAlarmManager: AlarmManager, private val mListene } else -> throw IllegalArgumentException("Illegal mode: $mode") } - mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + timeout, mTag, this, mHandler) - isScheduled = true - return true + return try { + mAlarmManager.setExact( + AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + timeout, mTag, this, mHandler + ) + isScheduled = true + true + }catch (e: SecurityException) { + //Not exempt from battery restrictions + isScheduled = false + false + } } fun cancel() { diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Context.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Context.kt index ae76a1b..4cad897 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Context.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Context.kt @@ -6,6 +6,7 @@ import android.app.Application import android.app.IApplicationThread import android.app.IServiceConnection import android.content.* +import android.content.Context.RECEIVER_EXPORTED import android.content.pm.LauncherApps import android.content.res.Configuration import android.database.ContentObserver @@ -16,13 +17,11 @@ import android.net.Uri import android.os.* import android.provider.Settings import android.text.format.DateFormat -import android.util.Log import android.util.TypedValue import androidx.annotation.AttrRes import androidx.annotation.ColorInt import androidx.core.content.ContextCompat import com.kieronquinn.app.ambientmusicmod.repositories.RemoteSettingsRepository -import com.kieronquinn.app.ambientmusicmod.repositories.RemoteSettingsRepositoryImpl import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -88,13 +87,22 @@ fun Context.broadcastReceiverAsFlow(vararg actions: String) = callbackFlow { } } actions.forEach { - registerReceiver(receiver, IntentFilter(it)) + registerReceiverCompat(receiver, IntentFilter(it)) } awaitClose { unregisterReceiver(receiver) } } +@SuppressLint("UnspecifiedRegisterReceiverFlag") +fun Context.registerReceiverCompat(receiver: BroadcastReceiver, intentFilter: IntentFilter) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + registerReceiver(receiver, intentFilter, RECEIVER_EXPORTED) + }else{ + registerReceiver(receiver, intentFilter) + } +} + fun Context.batterySaverEnabled() = callbackFlow { val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager val receiver = object : BroadcastReceiver() { diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+EncryptedSharedPreferences.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+EncryptedSharedPreferences.kt new file mode 100644 index 0000000..57ec165 --- /dev/null +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+EncryptedSharedPreferences.kt @@ -0,0 +1,48 @@ +package com.kieronquinn.app.ambientmusicmod.utils.extensions + +import android.annotation.SuppressLint +import android.content.Context +import android.content.SharedPreferences +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import java.security.GeneralSecurityException +import java.security.KeyStore + +fun Context.createEncryptedSharedPrefDestructively( + fileName: String, onDelete: () -> Unit = {} +): SharedPreferences { + return try { + createEncryptedSharedPrefs(fileName) + } catch (e: GeneralSecurityException) { + deleteMasterKeyEntry() + deleteExistingPref(fileName) + onDelete() + createEncryptedSharedPrefs(fileName) + } +} + +@SuppressLint("ApplySharedPref") +private fun Context.deleteExistingPref(fileName: String) { + deleteSharedPreferences(fileName) +} + +private fun deleteMasterKeyEntry() { + KeyStore.getInstance("AndroidKeyStore").apply { + load(null) + deleteEntry(MasterKey.DEFAULT_MASTER_KEY_ALIAS) + } +} + +private fun Context.createEncryptedSharedPrefs(fileName: String): SharedPreferences { + val masterKey = MasterKey.Builder(this, MasterKey.DEFAULT_MASTER_KEY_ALIAS) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + + return EncryptedSharedPreferences.create( + this, + fileName, + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Inset.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Inset.kt index e623f1e..cb6df6a 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Inset.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Inset.kt @@ -4,7 +4,11 @@ import android.content.Context import android.os.Build import android.view.View import android.view.ViewGroup -import androidx.core.view.* +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams +import androidx.core.view.updateMargins +import androidx.core.view.updatePadding import com.kieronquinn.app.ambientmusicmod.R fun View.onApplyInsets(block: (view: View, insets: WindowInsetsCompat) -> Unit) { @@ -12,12 +16,13 @@ fun View.onApplyInsets(block: (view: View, insets: WindowInsetsCompat) -> Unit) block(view, insets) insets } + ViewCompat.getRootWindowInsets(this)?.let { block(this, it) } } fun View.applyBottomNavigationInset(extraPadding: Float = 0f) { val bottomNavHeight = resources.getDimension(R.dimen.bottom_nav_height).toInt() updatePadding(bottom = bottomNavHeight + extraPadding.toInt()) - val legacyWorkaround = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + val legacyWorkaround = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { context.getLegacyWorkaroundNavBarHeight() } else 0 onApplyInsets { _, insets -> @@ -29,7 +34,7 @@ fun View.applyBottomNavigationInset(extraPadding: Float = 0f) { fun View.applyBottomNavigationMargin(extraPadding: Float = 0f) { val bottomNavHeight = resources.getDimension(R.dimen.bottom_nav_height_margins).toInt() - val legacyWorkaround = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + val legacyWorkaround = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { context.getLegacyWorkaroundNavBarHeight() } else 0 onApplyInsets { _, insets -> @@ -43,7 +48,7 @@ fun View.applyBottomNavigationMargin(extraPadding: Float = 0f) { fun View.applyBottomNavigationMarginShort(extraPadding: Float = 0f) { val bottomNavHeight = resources.getDimension(R.dimen.bottom_nav_height).toInt() - val legacyWorkaround = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + val legacyWorkaround = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { context.getLegacyWorkaroundNavBarHeight() } else 0 onApplyInsets { _, insets -> diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Keyguard.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Keyguard.kt index 6f0ad8d..9bdd6dc 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Keyguard.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Keyguard.kt @@ -13,7 +13,7 @@ fun Context.onKeyguardStateChanged() = callbackFlow { trySend(Unit) } } - registerReceiver( + registerReceiverCompat( receiver, IntentFilter().apply { addAction(Intent.ACTION_SCREEN_OFF) diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Service.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Service.kt new file mode 100644 index 0000000..89a4936 --- /dev/null +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Service.kt @@ -0,0 +1,23 @@ +package com.kieronquinn.app.ambientmusicmod.utils.extensions + +import android.app.Notification +import android.app.Service +import android.content.pm.ServiceInfo +import android.os.Build +import com.kieronquinn.app.ambientmusicmod.components.notifications.NotificationId + +fun Service.startForeground(notificationId: NotificationId, notification: Notification) { + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + startForeground( + notificationId.ordinal, + notification, + ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE + ) + }else{ + startForeground(notificationId.ordinal, notification) + } + }catch (e: Exception) { + //Caches ForegroundServiceStartNotAllowedException on S+ when unable to startForeground + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Wallpaper.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Wallpaper.kt index 7ae4348..ec96190 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Wallpaper.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/utils/extensions/Extensions+Wallpaper.kt @@ -20,7 +20,7 @@ fun Context.wallpaperSupportsDarkText() = callbackFlow { } } trySend(wallpaperManager.supportsDarkText()) - registerReceiver(receiver, IntentFilter(Intent.ACTION_WALLPAPER_CHANGED)) + registerReceiverCompat(receiver, IntentFilter(Intent.ACTION_WALLPAPER_CHANGED)) awaitClose { unregisterReceiver(receiver) } diff --git a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/work/PeriodicBackupWorker.kt b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/work/PeriodicBackupWorker.kt index 51bfe70..a1cfc5e 100644 --- a/app/src/main/java/com/kieronquinn/app/ambientmusicmod/work/PeriodicBackupWorker.kt +++ b/app/src/main/java/com/kieronquinn/app/ambientmusicmod/work/PeriodicBackupWorker.kt @@ -39,6 +39,8 @@ class PeriodicBackupWorker( companion object { private val TAG = PeriodicBackupWorker::class.java.simpleName private val URI_DEEP_LINK = Uri.parse("amm://backuprestore") + private const val PERIODIC_BACKUP_NAME = "amm_backup_periodic.ammbkp" + private const val PERIODIC_BACKUP_NAME_TMP = "amm_backup_periodic.ammbkp.tmp" fun enqueueOrCancelWorker( workManager: WorkManager, @@ -137,7 +139,9 @@ class PeriodicBackupWorker( is BackupRestoreRepository.BackupState.BackupComplete -> { if (it.result == BackupRestoreRepository.BackupResult.SUCCESS) { notificationManager.cancel(NotificationId.BACKUP.ordinal) + deleteTempFileIfExists() } else { + restoreTempFileIfExists() showErrorNotification(it.result) } settingsRepository.periodicBackupLastBackup.set( @@ -162,26 +166,36 @@ class PeriodicBackupWorker( } } - private suspend fun getBackupUri(): Uri? { + private suspend fun getBackupFolder(): DocumentFile? { val uri = settingsRepository.periodicBackupUri.get().takeIf { it.isNotBlank() } ?: return null return try { val folder = DocumentFile.fromTreeUri(context, Uri.parse(uri)) ?: return null - folder.createDeletingIfExists( - "application/ammbkp", "amm_backup_periodic.ammbkp" - )?.uri + folder.createRenamingIfExists("application/ammbkp", PERIODIC_BACKUP_NAME) }catch (e: Exception){ null } } - private fun DocumentFile.createDeletingIfExists( + private suspend fun getBackupUri(): Uri? { + return getBackupFolder()?.uri + } + + private fun DocumentFile.createRenamingIfExists( mimeType: String, fileName: String ): DocumentFile? { - if(findFile(fileName)?.delete() == false) return null + findFile(fileName)?.renameTo(PERIODIC_BACKUP_NAME_TMP) return createFile(mimeType, fileName) } + private suspend fun deleteTempFileIfExists() { + getBackupFolder()?.findFile(PERIODIC_BACKUP_NAME)?.delete() + } + + private suspend fun restoreTempFileIfExists() { + getBackupFolder()?.findFile(PERIODIC_BACKUP_NAME_TMP)?.renameTo(PERIODIC_BACKUP_NAME) + } + private fun Context.showErrorNotification( backupResult: BackupRestoreRepository.BackupResult ) = createNotification(NotificationChannel.BACKUP) { diff --git a/app/src/main/res/raw/faq.md b/app/src/main/res/raw/faq.md index 7552099..7b18f4b 100644 --- a/app/src/main/res/raw/faq.md +++ b/app/src/main/res/raw/faq.md @@ -142,7 +142,9 @@ reasons), so the overlay cannot be hidden during this time. ## Can I show Now Playing on the Always On (Ambient) Display? -No, it is not possible to display overlays on Ambient Displays. +Not directly, as it is not possible to display overlays on Ambient Displays, but if your device has +an Always on Display supported by [Smartspacer](https://github.com/KieronQuinn/Smartspacer), you can +use that to display Now Playing on the AoD. ## Does Ambient Music Mod work with third party Now Playing History apps? diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 80fa8fb..b67287a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -376,7 +376,6 @@ Access Token Your access token is: %1s\n\nTap to copy, long press to reset.\n\nCheck the Wiki page for more info on how to use this token. Copied! - Failed to load settings, you may need to reinstall Ambient Music Mod Bedtime Disable at Bedtime diff --git a/app/src/main/res/xml/samsung_widget.xml b/app/src/main/res/xml/samsung_widget.xml new file mode 100644 index 0000000..13acdc8 --- /dev/null +++ b/app/src/main/res/xml/samsung_widget.xml @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/astrea/build.gradle b/astrea/build.gradle index c4a4f6a..bdc6eaa 100644 --- a/astrea/build.gradle +++ b/astrea/build.gradle @@ -30,6 +30,7 @@ android { kotlinOptions { jvmTarget = 17 } + namespace 'com.kieronquinn.app.astrea' } protobuf { diff --git a/astrea/src/main/AndroidManifest.xml b/astrea/src/main/AndroidManifest.xml index 2218091..d95fdb5 100644 --- a/astrea/src/main/AndroidManifest.xml +++ b/astrea/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + diff --git a/build.gradle b/build.gradle index aa891db..3383c1e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,10 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.nav_version = "2.5.3" - ext.protobufVersion = '0.9.1' - ext.shizuku_version = '12.1.0' - ext.refine_version = '4.1.0' + ext.nav_version = "2.7.6" + ext.protobufVersion = '0.9.4' + ext.shizuku_version = '13.1.5' + ext.refine_version = '4.4.0' repositories { google() } @@ -16,11 +16,12 @@ buildscript { } plugins { - id 'com.android.application' version '7.4.2' apply false - id 'com.android.library' version '7.4.2' apply false - id 'org.jetbrains.kotlin.android' version '1.8.20' apply false + id 'com.android.application' version '8.2.2' apply false + id 'com.android.library' version '8.2.2' apply false + id 'org.jetbrains.kotlin.android' version '1.9.22' apply false id 'com.google.dagger.hilt.android' version '2.41' apply false id 'dev.rikka.tools.refine' version '4.1.0' apply false + id 'com.google.devtools.ksp' version '1.9.22-1.0.17' apply false } task clean(type: Delete) { diff --git a/gradle.properties b/gradle.properties index 571f6dd..e9801e1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,4 +21,6 @@ kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e9a4f9c..3e215fe 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Apr 13 20:49:47 BST 2023 +#Sat Feb 03 19:53:48 GMT 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/ondemandoverlay/build.gradle b/ondemandoverlay/build.gradle index 93d4744..4c18e80 100644 --- a/ondemandoverlay/build.gradle +++ b/ondemandoverlay/build.gradle @@ -58,6 +58,7 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + namespace 'com.kieronquinn.app.ondemandoverlay' } dependencies {} diff --git a/ondemandoverlay/src/main/AndroidManifest.xml b/ondemandoverlay/src/main/AndroidManifest.xml index e412b52..4483900 100644 --- a/ondemandoverlay/src/main/AndroidManifest.xml +++ b/ondemandoverlay/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ - + \ No newline at end of file