From 52b32d0fd6ece41f1cae04ad0271be95e3f5060b Mon Sep 17 00:00:00 2001 From: Yahor Berdnikau Date: Sat, 23 Jun 2018 22:18:08 +0200 Subject: [PATCH] Add DI for MusicService. It is still hidden behind MusicServiceFactory, but opens a way to use it directly via injection. Signed-off-by: Yahor Berdnikau --- dependencies.gradle | 3 + ultrasonic/build.gradle | 1 + ultrasonic/src/main/AndroidManifest.xml | 1 + .../fragment/ServerSettingsFragment.java | 6 +- .../service/MusicServiceFactory.java | 150 ------------------ .../java/org/moire/ultrasonic/util/Util.java | 16 +- .../kotlin/org/moire/ultrasonic/app/UApp.kt | 19 +++ .../ultrasonic/cache/AndroidDirectories.kt | 17 ++ .../moire/ultrasonic/di/DirectoriesModule.kt | 9 ++ .../moire/ultrasonic/di/MusicServiceModule.kt | 107 +++++++++++++ .../ultrasonic/service/MusicServiceFactory.kt | 56 +++++++ 11 files changed, 226 insertions(+), 159 deletions(-) delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/service/MusicServiceFactory.java create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/cache/AndroidDirectories.kt create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/DirectoriesModule.kt create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt create mode 100644 ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MusicServiceFactory.kt diff --git a/dependencies.gradle b/dependencies.gradle index ce6e4309..2e38bee1 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -20,6 +20,7 @@ ext.versions = [ okhttp : "3.10.0", semver : "1.0.0", twitterSerial : "0.1.6", + koin : "0.9.3", junit : "4.12", mockito : "2.16.0", @@ -51,6 +52,8 @@ ext.other = [ okhttpLogging : "com.squareup.okhttp3:logging-interceptor:$versions.okhttp", semver : "net.swiftzer.semver:semver:$versions.semver", twitterSerial : "com.twitter.serial:serial:$versions.twitterSerial", + koinCore : "org.koin:koin-core:$versions.koin", + koinAndroid : "org.koin:koin-android:$versions.koin" ] ext.testing = [ diff --git a/ultrasonic/build.gradle b/ultrasonic/build.gradle index 7cd7c804..3bbd5475 100644 --- a/ultrasonic/build.gradle +++ b/ultrasonic/build.gradle @@ -61,6 +61,7 @@ dependencies { implementation androidSupport.design implementation other.kotlinStdlib + implementation other.koinAndroid testImplementation other.kotlinReflect testImplementation testing.junit diff --git a/ultrasonic/src/main/AndroidManifest.xml b/ultrasonic/src/main/AndroidManifest.xml index 6c158318..11730690 100644 --- a/ultrasonic/src/main/AndroidManifest.xml +++ b/ultrasonic/src/main/AndroidManifest.xml @@ -23,6 +23,7 @@ a:icon="@mipmap/ic_launcher" a:roundIcon="@mipmap/ic_launcher_round" a:theme="@style/Theme.AppCompat" + a:name=".app.UApp" a:label="@string/common.appname"> . - - Copyright 2009 (C) Sindre Mehus - */ -package org.moire.ultrasonic.service; - -import android.content.Context; -import android.content.SharedPreferences; -import android.util.Log; - -import org.jetbrains.annotations.NotNull; -import org.moire.ultrasonic.BuildConfig; -import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient; -import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions; -import org.moire.ultrasonic.cache.Directories; -import org.moire.ultrasonic.cache.PermanentFileStorage; -import org.moire.ultrasonic.util.Constants; -import org.moire.ultrasonic.util.Util; - -import java.io.File; - -/** - * @author Sindre Mehus - * @version $Id$ - */ -public class MusicServiceFactory { - private static final String LOG_TAG = MusicServiceFactory.class.getSimpleName(); - private static MusicService REST_MUSIC_SERVICE = null; - private static MusicService OFFLINE_MUSIC_SERVICE = null; - - public static MusicService getMusicService(Context context) { - if (Util.isOffline(context)) { - Log.d(LOG_TAG, "App is offline, returning offline music service."); - if (OFFLINE_MUSIC_SERVICE == null) { - synchronized (MusicServiceFactory.class) { - if (OFFLINE_MUSIC_SERVICE == null) { - Log.d(LOG_TAG, "Creating new offline music service"); - OFFLINE_MUSIC_SERVICE = new OfflineMusicService( - createSubsonicApiClient(context), - getPermanentFileStorage(context)); - } - } - } - - return OFFLINE_MUSIC_SERVICE; - } else { - Log.d(LOG_TAG, "Returning rest music service"); - if (REST_MUSIC_SERVICE == null) { - synchronized (MusicServiceFactory.class) { - if (REST_MUSIC_SERVICE == null) { - Log.d(LOG_TAG, "Creating new rest music service"); - REST_MUSIC_SERVICE = new CachedMusicService(new RESTMusicService( - createSubsonicApiClient(context), - getPermanentFileStorage(context))); - } - } - } - - return REST_MUSIC_SERVICE; - } - } - - /** - * Resets {@link MusicService} to initial state, so on next call to {@link #getMusicService(Context)} - * it will return updated instance of it. - */ - public static void resetMusicService() { - Log.d(LOG_TAG, "Resetting music service"); - synchronized (MusicServiceFactory.class) { - REST_MUSIC_SERVICE = null; - OFFLINE_MUSIC_SERVICE = null; - } - } - - private static SubsonicAPIClient createSubsonicApiClient(final Context context) { - final SharedPreferences preferences = Util.getPreferences(context); - int instance = preferences.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1); - String serverUrl = preferences.getString(Constants.PREFERENCES_KEY_SERVER_URL + instance, null); - String username = preferences.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null); - String password = preferences.getString(Constants.PREFERENCES_KEY_PASSWORD + instance, null); - boolean allowSelfSignedCertificate = preferences - .getBoolean(Constants.PREFERENCES_KEY_ALLOW_SELF_SIGNED_CERTIFICATE + instance, false); - boolean enableLdapUserSupport = preferences - .getBoolean(Constants.PREFERENCES_KEY_LDAP_SUPPORT + instance , false); - - if (serverUrl == null || - username == null || - password == null) { - Log.i("MusicServiceFactory", "Server credentials is not available"); - return new SubsonicAPIClient("http://localhost", "", "", - SubsonicAPIVersions.fromApiVersion(Constants.REST_PROTOCOL_VERSION), - Constants.REST_CLIENT_ID, allowSelfSignedCertificate, - enableLdapUserSupport, BuildConfig.DEBUG); - } - - return new SubsonicAPIClient(serverUrl, username, password, - SubsonicAPIVersions.fromApiVersion(Constants.REST_PROTOCOL_VERSION), - Constants.REST_CLIENT_ID, allowSelfSignedCertificate, - enableLdapUserSupport, BuildConfig.DEBUG); - } - - private static PermanentFileStorage getPermanentFileStorage(final Context context) { - final SharedPreferences preferences = Util.getPreferences(context); - int instance = preferences.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1); - final String serverId = getServerId(preferences, instance); - - return new PermanentFileStorage(getDirectories(context), serverId, BuildConfig.DEBUG); - } - - public static String getServerId(final SharedPreferences sp, final int instance) { - String serverUrl = sp.getString( - Constants.PREFERENCES_KEY_SERVER_URL + instance, null); - return String.valueOf(Math.abs((serverUrl + instance).hashCode())); - } - - public static Directories getDirectories(final Context context) { - return new Directories() { - @NotNull - @Override - public File getInternalCacheDir() { - return context.getCacheDir(); - } - - @NotNull - @Override - public File getInternalDataDir() { - return context.getFilesDir(); - } - - @Override - public File getExternalCacheDir() { - return context.getExternalCacheDir(); - } - }; - } -} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java index 90620e00..e9673e23 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Util.java @@ -171,14 +171,16 @@ public class Util extends DownloadActivity return preferences.getBoolean(Constants.PREFERENCES_KEY_SHOW_LOCK_SCREEN_CONTROLS, false); } - public static void setActiveServer(Context context, int instance) - { + public static void setActiveServer( + Context context, + int instance + ) { MusicServiceFactory.resetMusicService(); - SharedPreferences preferences = getPreferences(context); - SharedPreferences.Editor editor = preferences.edit(); - editor.putInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, instance); - editor.commit(); - } + SharedPreferences preferences = getPreferences(context); + SharedPreferences.Editor editor = preferences.edit(); + editor.putInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, instance); + editor.apply(); + } public static int getActiveServer(Context context) { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt new file mode 100644 index 00000000..db151566 --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt @@ -0,0 +1,19 @@ +package org.moire.ultrasonic.app + +import android.app.Application +import org.koin.android.ext.android.startKoin +import org.moire.ultrasonic.di.directoriesModule +import org.moire.ultrasonic.di.musicServiceModule +import org.moire.ultrasonic.util.Util + +class UApp : Application() { + override fun onCreate() { + super.onCreate() + + val sharedPreferences = Util.getPreferences(this) + startKoin(this, listOf( + directoriesModule, + musicServiceModule(sharedPreferences) + )) + } +} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/cache/AndroidDirectories.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/cache/AndroidDirectories.kt new file mode 100644 index 00000000..d8a815a7 --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/cache/AndroidDirectories.kt @@ -0,0 +1,17 @@ +package org.moire.ultrasonic.cache + +import android.content.Context +import java.io.File + +/** + * Provides specific to Android implementation of [Directories]. + */ +class AndroidDirectories( + private val context: Context +) : Directories { + override fun getInternalCacheDir(): File = context.cacheDir + + override fun getInternalDataDir(): File = context.filesDir + + override fun getExternalCacheDir(): File? = context.externalCacheDir +} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/DirectoriesModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/DirectoriesModule.kt new file mode 100644 index 00000000..ddbe21f0 --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/DirectoriesModule.kt @@ -0,0 +1,9 @@ +package org.moire.ultrasonic.di + +import org.koin.dsl.module.applicationContext +import org.moire.ultrasonic.cache.AndroidDirectories +import org.moire.ultrasonic.cache.Directories + +val directoriesModule = applicationContext { + bean { AndroidDirectories(get()) } bind Directories::class +} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt new file mode 100644 index 00000000..57fa1607 --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt @@ -0,0 +1,107 @@ +@file:JvmName("MusicServiceModule") +package org.moire.ultrasonic.di + +import android.content.SharedPreferences +import android.util.Log +import org.koin.dsl.module.applicationContext +import org.moire.ultrasonic.BuildConfig +import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient +import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions +import org.moire.ultrasonic.cache.PermanentFileStorage +import org.moire.ultrasonic.service.CachedMusicService +import org.moire.ultrasonic.service.MusicService +import org.moire.ultrasonic.service.OfflineMusicService +import org.moire.ultrasonic.service.RESTMusicService +import org.moire.ultrasonic.util.Constants +import kotlin.math.abs + +internal const val MUSIC_SERVICE_CONTEXT = "CurrentMusicService" +internal const val ONLINE_MUSIC_SERVICE = "OnlineMusicService" +internal const val OFFLINE_MUSIC_SERVICE = "OfflineMusicService" +private const val DEFAULT_SERVER_INSTANCE = 1 +private const val UNKNOWN_SERVER_URL = "not-exists" +private const val LOG_TAG = "MusicServiceModule" + +fun musicServiceModule(sp: SharedPreferences) = applicationContext { + context(MUSIC_SERVICE_CONTEXT) { + bean(name = "ServerInstance") { + return@bean sp.getInt( + Constants.PREFERENCES_KEY_SERVER_INSTANCE, + DEFAULT_SERVER_INSTANCE + ) + } + + bean(name = "ServerID") { + val serverInstance = get(name = "ServerInstance") + val serverUrl = sp.getString( + Constants.PREFERENCES_KEY_SERVER_URL + serverInstance, + null + ) + return@bean if (serverUrl == null) { + UNKNOWN_SERVER_URL + } else { + abs("$serverUrl$serverInstance".hashCode()).toString() + } + } + + bean { + val serverId = get(name = "ServerID") + return@bean PermanentFileStorage(get(), serverId, BuildConfig.DEBUG) + } + + bean { + val instance = get(name = "ServerInstance") + val serverUrl = sp.getString(Constants.PREFERENCES_KEY_SERVER_URL + instance, null) + val username = sp.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null) + val password = sp.getString(Constants.PREFERENCES_KEY_PASSWORD + instance, null) + val allowSelfSignedCertificate = sp.getBoolean( + Constants.PREFERENCES_KEY_ALLOW_SELF_SIGNED_CERTIFICATE + instance, + false + ) + val enableLdapUserSupport = sp.getBoolean( + Constants.PREFERENCES_KEY_LDAP_SUPPORT + instance, + false + ) + + if (serverUrl == null || + username == null || + password == null + ) { + Log.i(LOG_TAG, "Server credentials is not available") + return@bean SubsonicAPIClient( + baseUrl = "http://localhost", + username = "", + password = "", + minimalProtocolVersion = SubsonicAPIVersions.fromApiVersion( + Constants.REST_PROTOCOL_VERSION + ), + clientID = Constants.REST_CLIENT_ID, + allowSelfSignedCertificate = allowSelfSignedCertificate, + enableLdapUserSupport = enableLdapUserSupport, + debug = BuildConfig.DEBUG + ) + } else { + return@bean SubsonicAPIClient( + baseUrl = serverUrl, + username = username, + password = password, + minimalProtocolVersion = SubsonicAPIVersions.fromApiVersion( + Constants.REST_PROTOCOL_VERSION + ), + clientID = Constants.REST_CLIENT_ID, + allowSelfSignedCertificate = allowSelfSignedCertificate, + enableLdapUserSupport = enableLdapUserSupport, + debug = BuildConfig.DEBUG + ) + } + } + + bean(name = ONLINE_MUSIC_SERVICE) { + return@bean CachedMusicService(RESTMusicService(get(), get())) + } + + bean(name = OFFLINE_MUSIC_SERVICE) { + return@bean OfflineMusicService(get(), get()) + } + } +} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MusicServiceFactory.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MusicServiceFactory.kt new file mode 100644 index 00000000..5fbb393e --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MusicServiceFactory.kt @@ -0,0 +1,56 @@ +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + + Copyright 2009 (C) Sindre Mehus + */ +package org.moire.ultrasonic.service + +import android.content.Context +import org.koin.standalone.KoinComponent +import org.koin.standalone.get +import org.koin.standalone.releaseContext +import org.moire.ultrasonic.cache.Directories +import org.moire.ultrasonic.di.MUSIC_SERVICE_CONTEXT +import org.moire.ultrasonic.di.OFFLINE_MUSIC_SERVICE +import org.moire.ultrasonic.di.ONLINE_MUSIC_SERVICE +import org.moire.ultrasonic.util.Util + +@Deprecated("Use DI way to get MusicService") +object MusicServiceFactory : KoinComponent { + @JvmStatic + fun getMusicService(context: Context): MusicService { + return if (Util.isOffline(context)) { + get(OFFLINE_MUSIC_SERVICE) + } else { + get(ONLINE_MUSIC_SERVICE) + } + } + + /** + * Resets [MusicService] to initial state, so on next call to [.getMusicService] + * it will return updated instance of it. + */ + @JvmStatic + fun resetMusicService() { + releaseContext(MUSIC_SERVICE_CONTEXT) + } + + @JvmStatic + fun getServerId() = get(name = "ServerID") + + @JvmStatic + fun getDirectories() = get() +}