Skip to content

Commit

Permalink
- Fixed screen on recognition not working on some devices
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
KieronQuinn committed Feb 3, 2024
1 parent 7275371 commit ab1df75
Show file tree
Hide file tree
Showing 35 changed files with 281 additions and 173 deletions.
43 changes: 23 additions & 20 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -41,7 +42,7 @@ android {
defaultConfig {
applicationId "com.kieronquinn.app.ambientmusicmod"
minSdk 28
targetSdk 33
targetSdk 34
versionCode version
versionName tagName

Expand Down Expand Up @@ -86,7 +87,9 @@ android {
}
buildFeatures {
viewBinding true
aidl true
}
namespace 'com.kieronquinn.app.ambientmusicmod'
}

protobuf {
Expand All @@ -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'
Expand All @@ -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'
Expand Down Expand Up @@ -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'
Expand All @@ -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'
Expand Down
4 changes: 2 additions & 2 deletions app/release/output-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 230,
"versionName": "2.3",
"versionCode": 232,
"versionName": "2.3.2",
"outputFile": "app-release.apk"
}
],
Expand Down
13 changes: 9 additions & 4 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.kieronquinn.app.ambientmusicmod">
xmlns:tools="http://schemas.android.com/tools">

<permission
android:name="com.kieronquinn.app.ambientmusicmod.ACCESS_SERVICE"
Expand All @@ -12,6 +11,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
Expand Down Expand Up @@ -79,7 +79,8 @@

<receiver
android:name=".receivers.ExternalAccessReceiver"
android:exported="true">
android:exported="true"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="${applicationId}.action.ENABLE" />
<action android:name="${applicationId}.action.DISABLE" />
Expand All @@ -89,7 +90,8 @@
</intent-filter>
</receiver>

<service android:name=".service.AmbientMusicModForegroundService" />
<service android:name=".service.AmbientMusicModForegroundService"
android:foregroundServiceType="specialUse" />

<service
android:name=".service.QuickSettingTileService"
Expand Down Expand Up @@ -159,6 +161,9 @@
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidgetprovider_dynamic" />
<meta-data
android:name="com.samsung.android.appwidget.provider"
android:resource="@xml/samsung_widget" />
</receiver>

<!-- 4x1 widget (< Android 12) -->
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Boolean>
val externalAccessRequireToken: AmbientMusicModSetting<Boolean>
val externalAccessToken: AmbientMusicModSetting<String>
val externalAccessToggleEnabled: AmbientMusicModSetting<Boolean>
val externalAccessRecognitionEnabled: AmbientMusicModSetting<Boolean>

}

class EncryptedSettingsRepositoryImpl(
Expand All @@ -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)

Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -64,7 +65,11 @@ class ExternalAccessRepositoryImpl(
recognitionRepository.requestOnDemandRecognition()
}else{
recognitionRepository.requestRecognition()
}.launchIn(scope)
}.collect {
if(it is RecognitionState.Recognised){
AmbientMusicModForegroundService.sendManualRecognition(it)
}
}
}
}

Expand All @@ -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()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -162,7 +182,7 @@ class RemoteSettingsRepositoryImpl(
}
}
trySend(isEnabled())
context.registerReceiver(
context.registerReceiverCompat(
receiver, IntentFilter(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED)
)
awaitClose {
Expand Down
Loading

0 comments on commit ab1df75

Please sign in to comment.