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()
+}