From 5d122c3bc83b3a17d52ccc01e29e542316d08c33 Mon Sep 17 00:00:00 2001 From: Colin Edwards Date: Thu, 4 Jan 2024 17:36:58 -0600 Subject: [PATCH 1/2] android: Add dependencies and new application class for dagger / hilt support Dagger and hilt give us dependency injection which makes it easier to split up parts of the app. This lets us easily split out things like logging and paves the way to migrate off preferences to DataStore This also remove the process name on the service to pull eveything into one process so we don't have to do IPC to pass logs around. This lets us use the same instances of injected classes between the UI and the service side. --- android/app/build.gradle.kts | 8 +++++++- android/app/src/main/AndroidManifest.xml | 6 +++--- android/app/src/main/java/org/musicpd/MPDApplication.kt | 9 +++++++++ android/build.gradle.kts | 4 +++- 4 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 android/app/src/main/java/org/musicpd/MPDApplication.kt diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index cb0bb9771..1b88f67f8 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -1,6 +1,8 @@ plugins { id("com.android.application") id("org.jetbrains.kotlin.android") + id("com.google.devtools.ksp") + id("com.google.dagger.hilt.android") } android { @@ -24,7 +26,7 @@ android { } composeOptions { - kotlinCompilerExtensionVersion = "1.5.4" + kotlinCompilerExtensionVersion = "1.5.7" } buildTypes { @@ -64,6 +66,10 @@ dependencies { implementation("com.github.alorma:compose-settings-storage-preferences:1.0.3") implementation("com.google.accompanist:accompanist-permissions:0.33.2-alpha") + implementation("com.google.dagger:hilt-android:2.49") + ksp("com.google.dagger:dagger-compiler:2.49") + ksp("com.google.dagger:hilt-compiler:2.49") + // Android Studio Preview support implementation("androidx.compose.ui:ui-tooling-preview") debugImplementation("androidx.compose.ui:ui-tooling") diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 274c55e64..facfd24ca 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -25,7 +25,8 @@ android:label="@string/app_name" android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher_round" - android:theme="@style/Theme.MPD"> + android:theme="@style/Theme.MPD" + android:name=".MPDApplication"> @@ -53,8 +54,7 @@ + android:name=".Main" /> diff --git a/android/app/src/main/java/org/musicpd/MPDApplication.kt b/android/app/src/main/java/org/musicpd/MPDApplication.kt new file mode 100644 index 000000000..94d8709cc --- /dev/null +++ b/android/app/src/main/java/org/musicpd/MPDApplication.kt @@ -0,0 +1,9 @@ +package org.musicpd + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp +class MPDApplication : Application() { + +} \ No newline at end of file diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 1b4c7195f..32ac8c304 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -1,5 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { id("com.android.application") version "8.1.2" apply false - id("org.jetbrains.kotlin.android") version "1.9.20" apply false + id("org.jetbrains.kotlin.android") version "1.9.21" apply false + id("com.google.devtools.ksp") version "1.9.22-1.0.16" apply false + id("com.google.dagger.hilt.android") version "2.49" apply false } \ No newline at end of file From 324bd95c919c3b765ed004e552b652591a12ed92 Mon Sep 17 00:00:00 2001 From: Colin Edwards Date: Thu, 4 Jan 2024 17:41:26 -0600 Subject: [PATCH 2/2] android: Move logging into it's own repository class. Logs will be maintained and appended even when the main UI is not bound to the service. This also lets us log without filling a Handler with a bunch of messages we might just throw away anyway. --- .../main/aidl/org/musicpd/IMainCallback.aidl | 1 - .../app/src/main/java/org/musicpd/Main.java | 25 ++++++-------- .../org/musicpd/data/LoggingRepository.kt | 34 +++++++++++++++++++ .../java/org/musicpd/ui/SettingsActivity.kt | 8 ++--- .../java/org/musicpd/ui/SettingsViewModel.kt | 31 +++++------------ 5 files changed, 56 insertions(+), 43 deletions(-) create mode 100644 android/app/src/main/java/org/musicpd/data/LoggingRepository.kt diff --git a/android/app/src/main/aidl/org/musicpd/IMainCallback.aidl b/android/app/src/main/aidl/org/musicpd/IMainCallback.aidl index c8cdaa4a0..21e435d2a 100644 --- a/android/app/src/main/aidl/org/musicpd/IMainCallback.aidl +++ b/android/app/src/main/aidl/org/musicpd/IMainCallback.aidl @@ -5,5 +5,4 @@ interface IMainCallback void onStarted(); void onStopped(); void onError(String error); - void onLog(int priority, String msg); } diff --git a/android/app/src/main/java/org/musicpd/Main.java b/android/app/src/main/java/org/musicpd/Main.java index 7196e0c5f..78bf82fb5 100644 --- a/android/app/src/main/java/org/musicpd/Main.java +++ b/android/app/src/main/java/org/musicpd/Main.java @@ -3,7 +3,6 @@ package org.musicpd; -import android.annotation.TargetApi; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -21,15 +20,18 @@ import android.os.PowerManager; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Log; -import android.widget.RemoteViews; - -import androidx.core.app.ServiceCompat; +import org.musicpd.data.LoggingRepository; import org.musicpd.ui.SettingsActivity; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; + +@AndroidEntryPoint public class Main extends Service implements Runnable { private static final String TAG = "Main"; private static final String WAKELOCK_TAG = "mpd:wakelockmain"; @@ -39,7 +41,6 @@ public class Main extends Service implements Runnable { private static final int MAIN_STATUS_STARTED = 1; private static final int MSG_SEND_STATUS = 0; - private static final int MSG_SEND_LOG = 1; private Thread mThread = null; private int mStatus = MAIN_STATUS_STOPPED; @@ -50,6 +51,9 @@ public class Main extends Service implements Runnable { private boolean mPauseOnHeadphonesDisconnect = false; private PowerManager.WakeLock mWakelock = null; + @Inject + LoggingRepository logging; + static class MainStub extends IMain.Stub { private Main mService; MainStub(Main service) { @@ -98,9 +102,6 @@ public class Main extends Service implements Runnable { break; } break; - case MSG_SEND_LOG: - cb.onLog(arg1, (String) obj); - break; } } catch (RemoteException e) { } @@ -111,7 +112,7 @@ public class Main extends Service implements Runnable { private Bridge.LogListener mLogListener = new Bridge.LogListener() { @Override public void onLog(int priority, String msg) { - sendMessage(MSG_SEND_LOG, priority, 0, msg); + logging.addLogItem(priority, msg); } }; @@ -303,7 +304,6 @@ public class Main extends Service implements Runnable { public void onStarted(); public void onStopped(); public void onError(String error); - public void onLog(int priority, String msg); } private boolean mBound = false; @@ -327,11 +327,6 @@ public class Main extends Service implements Runnable { public void onError(String error) throws RemoteException { mCallback.onError(error); } - - @Override - public void onLog(int priority, String msg) throws RemoteException { - mCallback.onLog(priority, msg); - } }; private final ServiceConnection mServiceConnection = new ServiceConnection() { diff --git a/android/app/src/main/java/org/musicpd/data/LoggingRepository.kt b/android/app/src/main/java/org/musicpd/data/LoggingRepository.kt new file mode 100644 index 000000000..9e5dd9061 --- /dev/null +++ b/android/app/src/main/java/org/musicpd/data/LoggingRepository.kt @@ -0,0 +1,34 @@ +package org.musicpd.data + +import android.util.Log +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject +import javax.inject.Singleton + +private const val MAX_LOGS = 500 + +@Singleton +class LoggingRepository @Inject constructor() { + + private val _logItemFLow = MutableStateFlow(listOf()) + val logItemFLow: StateFlow> = _logItemFLow + + fun addLogItem(priority: Int, message: String) { + if (_logItemFLow.value.size > MAX_LOGS) { + _logItemFLow.value = _logItemFLow.value.drop(1) + } + + val priorityString: String = when (priority) { + Log.DEBUG -> "D" + Log.ERROR -> "E" + Log.INFO -> "I" + Log.VERBOSE -> "V" + Log.WARN -> "W" + else -> "" + } + + _logItemFLow.value = _logItemFLow.value + ("$priorityString/$message") + } + +} \ No newline at end of file diff --git a/android/app/src/main/java/org/musicpd/ui/SettingsActivity.kt b/android/app/src/main/java/org/musicpd/ui/SettingsActivity.kt index 56e4b0f92..a36fd57e8 100644 --- a/android/app/src/main/java/org/musicpd/ui/SettingsActivity.kt +++ b/android/app/src/main/java/org/musicpd/ui/SettingsActivity.kt @@ -37,12 +37,14 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.shouldShowRationale +import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.musicpd.Main import org.musicpd.R +@AndroidEntryPoint class SettingsActivity : ComponentActivity() { private val settingsViewModel: SettingsViewModel by viewModels() @@ -72,10 +74,6 @@ class SettingsActivity : ComponentActivity() { settingsViewModel.updateStatus(error, false) connectClient() } - - override fun onLog(priority: Int, msg: String) { - settingsViewModel.addLogItem(priority, msg) - } }) settingsViewModel.setClient(client) @@ -138,7 +136,7 @@ fun SettingsContainer(settingsViewModel: SettingsViewModel = viewModel()) { settingsViewModel.setPauseOnHeadphonesDisconnect(newValue) } ) - LogView(settingsViewModel.logItemFLow.collectAsStateWithLifecycle()) + LogView(settingsViewModel.getLogs().collectAsStateWithLifecycle()) } } } diff --git a/android/app/src/main/java/org/musicpd/ui/SettingsViewModel.kt b/android/app/src/main/java/org/musicpd/ui/SettingsViewModel.kt index 3d09370ee..6fc2c7002 100644 --- a/android/app/src/main/java/org/musicpd/ui/SettingsViewModel.kt +++ b/android/app/src/main/java/org/musicpd/ui/SettingsViewModel.kt @@ -1,23 +1,23 @@ package org.musicpd.ui import android.content.Context -import android.util.Log import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import org.musicpd.Main import org.musicpd.Preferences +import org.musicpd.data.LoggingRepository +import javax.inject.Inject -private const val MAX_LOGS = 500 - -class SettingsViewModel : ViewModel() { +@HiltViewModel +class SettingsViewModel @Inject constructor( + private var loggingRepository: LoggingRepository +) : ViewModel() { private var mClient: Main.Client? = null - private val _logItemFLow = MutableStateFlow(listOf()) - val logItemFLow: StateFlow> = _logItemFLow - data class StatusUiState( val statusMessage: String = "", val running: Boolean = false @@ -26,21 +26,8 @@ class SettingsViewModel : ViewModel() { private val _statusUIState = MutableStateFlow(StatusUiState()) val statusUIState: StateFlow = _statusUIState.asStateFlow() - fun addLogItem(priority: Int, message: String) { - if (_logItemFLow.value.size > MAX_LOGS) { - _logItemFLow.value = _logItemFLow.value.drop(1) - } - - val priorityString: String = when (priority) { - Log.DEBUG -> "D" - Log.ERROR -> "E" - Log.INFO -> "I" - Log.VERBOSE -> "V" - Log.WARN -> "W" - else -> "" - } - - _logItemFLow.value = _logItemFLow.value + ("$priorityString/$message") + fun getLogs(): StateFlow> { + return loggingRepository.logItemFLow } fun updateStatus(message: String, running: Boolean) {