diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java index cd7fcf46..be2783af 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java @@ -44,6 +44,7 @@ import org.moire.ultrasonic.domain.MusicDirectory; import org.moire.ultrasonic.domain.MusicDirectory.Entry; import org.moire.ultrasonic.domain.PlayerState; import org.moire.ultrasonic.domain.Share; +import org.moire.ultrasonic.featureflags.Feature; import org.moire.ultrasonic.service.*; import org.moire.ultrasonic.subsonic.SubsonicImageLoaderProxy; import org.moire.ultrasonic.util.*; @@ -790,10 +791,14 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen } } - public synchronized void clearImageLoader() - { - if (IMAGE_LOADER != null && IMAGE_LOADER.isRunning()) IMAGE_LOADER.clear(); - } + public synchronized void clearImageLoader() { + if (IMAGE_LOADER != null && + IMAGE_LOADER.isRunning()) { + IMAGE_LOADER.clear(); + } + + IMAGE_LOADER = null; + } public synchronized ImageLoader getImageLoader() { if (IMAGE_LOADER == null || @@ -802,10 +807,18 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen this, Util.getImageLoaderConcurrency(this) ); - IMAGE_LOADER = new SubsonicImageLoaderProxy( - legacyImageLoader, - ((UApp) getApplication()).getSubsonicImageLoader() - ); + + boolean isNewImageLoaderEnabled = ((UApp) getApplication()).getFeaturesStorage() + .isFeatureEnabled(Feature.NEW_IMAGE_DOWNLOADER); + if (isNewImageLoaderEnabled) { + IMAGE_LOADER = new SubsonicImageLoaderProxy( + legacyImageLoader, + ((UApp) getApplication()).getSubsonicImageLoader() + ); + } else { + IMAGE_LOADER = legacyImageLoader; + } + IMAGE_LOADER.startImageLoader(); } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java index 472c49d1..98feabc8 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java @@ -4,29 +4,21 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceCategory; -import android.preference.PreferenceFragment; -import android.preference.PreferenceManager; +import android.preference.*; import android.provider.SearchRecentSuggestions; import android.support.annotation.Nullable; import android.util.Log; import android.view.View; - import org.moire.ultrasonic.R; import org.moire.ultrasonic.activity.ServerSettingsActivity; import org.moire.ultrasonic.activity.SubsonicTabActivity; +import org.moire.ultrasonic.app.UApp; +import org.moire.ultrasonic.featureflags.Feature; +import org.moire.ultrasonic.featureflags.FeatureStorage; import org.moire.ultrasonic.provider.SearchSuggestionProvider; import org.moire.ultrasonic.service.DownloadService; import org.moire.ultrasonic.service.DownloadServiceImpl; -import org.moire.ultrasonic.util.Constants; -import org.moire.ultrasonic.util.FileUtil; -import org.moire.ultrasonic.util.ImageLoader; -import org.moire.ultrasonic.util.TimeSpanPreference; -import org.moire.ultrasonic.util.Util; +import org.moire.ultrasonic.util.*; import java.io.File; @@ -115,6 +107,7 @@ public class SettingsFragment extends PreferenceFragment sharingDefaultGreeting.setText(Util.getShareGreeting(getActivity())); setupClearSearchPreference(); setupGaplessControlSettingsV14(); + setupFeatureFlagsPreferences(); } @Override @@ -178,6 +171,24 @@ public class SettingsFragment extends PreferenceFragment } } + private void setupFeatureFlagsPreferences() { + CheckBoxPreference ffImageLoader = (CheckBoxPreference) findPreference( + Constants.PREFERENCES_KEY_FF_IMAGE_LOADER); + + final FeatureStorage featureStorage = ((UApp) getActivity().getApplication()).getFeaturesStorage(); + if (ffImageLoader != null) { + ffImageLoader.setChecked(featureStorage.isFeatureEnabled(Feature.NEW_IMAGE_DOWNLOADER)); + ffImageLoader.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object o) { + featureStorage.changeFeatureFlag(Feature.NEW_IMAGE_DOWNLOADER, (Boolean) o); + ((SubsonicTabActivity) getActivity()).clearImageLoader(); + return true; + } + }); + } + } + private void setupGaplessControlSettingsV14() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { PreferenceCategory playbackControlSettings = diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java index e183f878..2fdb0c79 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/Constants.java @@ -130,6 +130,7 @@ public final class Constants public static final String PREFERENCES_KEY_SHOW_ALL_SONGS_BY_ARTIST = "showAllSongsByArtist"; public static final String PREFERENCES_KEY_SCAN_MEDIA = "scanMedia"; public static final String PREFERENCES_KEY_IMAGE_LOADER_CONCURRENCY = "imageLoaderConcurrency"; + public static final String PREFERENCES_KEY_FF_IMAGE_LOADER = "ff_new_image_loader"; // Number of free trial days for non-licensed servers. public static final int FREE_TRIAL_DAYS = 30; diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt index 97fcc8bb..8eb735b6 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt @@ -5,7 +5,9 @@ import org.koin.android.ext.android.get import org.koin.android.ext.android.startKoin import org.moire.ultrasonic.di.baseNetworkModule import org.moire.ultrasonic.di.directoriesModule +import org.moire.ultrasonic.di.featureFlagsModule import org.moire.ultrasonic.di.musicServiceModule +import org.moire.ultrasonic.featureflags.FeatureStorage import org.moire.ultrasonic.subsonic.loader.image.SubsonicImageLoader import org.moire.ultrasonic.util.Util @@ -17,6 +19,7 @@ class UApp : Application() { startKoin(this, listOf( directoriesModule, baseNetworkModule, + featureFlagsModule(this), musicServiceModule(sharedPreferences, this) )) } @@ -27,4 +30,11 @@ class UApp : Application() { fun getSubsonicImageLoader(): SubsonicImageLoader { return get() } + + /** + * Temporary method to get features storage. + */ + fun getFeaturesStorage(): FeatureStorage { + return get() + } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/FeatureFlagsModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/FeatureFlagsModule.kt new file mode 100644 index 00000000..81a1b09f --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/FeatureFlagsModule.kt @@ -0,0 +1,11 @@ +package org.moire.ultrasonic.di + +import android.content.Context +import org.koin.dsl.module.applicationContext +import org.moire.ultrasonic.featureflags.FeatureStorage + +fun featureFlagsModule( + context: Context +) = applicationContext { + factory { FeatureStorage(context) } +} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/featureflags/Feature.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/featureflags/Feature.kt new file mode 100644 index 00000000..1f0757a3 --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/featureflags/Feature.kt @@ -0,0 +1,14 @@ +package org.moire.ultrasonic.featureflags + +/** + * Contains a list of new features/implementations that are not yet finished, + * but possible to try it out. + */ +enum class Feature( + val defaultValue: Boolean +) { + /** + * Enables new image downloader implementation. + */ + NEW_IMAGE_DOWNLOADER(false) +} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/featureflags/FeatureStorage.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/featureflags/FeatureStorage.kt new file mode 100644 index 00000000..dca9bb4f --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/featureflags/FeatureStorage.kt @@ -0,0 +1,31 @@ +package org.moire.ultrasonic.featureflags + +import android.content.Context + +private const val SP_NAME = "feature_flags" + +/** + * Provides storage for current feature flag state. + */ +class FeatureStorage( + context: Context +) { + private val sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE) + + /** + * Get [feature] current enabled state. + */ + fun isFeatureEnabled(feature: Feature): Boolean { + return sp.getBoolean(feature.name, feature.defaultValue) + } + + /** + * Update [feature] enabled state to [isEnabled]. + */ + fun changeFeatureFlag( + feature: Feature, + isEnabled: Boolean + ) { + sp.edit().putBoolean(feature.name, isEnabled).apply() + } +} diff --git a/ultrasonic/src/main/res/values-es/strings.xml b/ultrasonic/src/main/res/values-es/strings.xml index 9ef97bff..58599912 100644 --- a/ultrasonic/src/main/res/values-es/strings.xml +++ b/ultrasonic/src/main/res/values-es/strings.xml @@ -436,5 +436,10 @@ <string name="api.subsonic.trial_period_is_over">El período de prueba ha terminado.</string> <string name="api.subsonic.upgrade_client">Versiones incompatibles. Por favor actualiza la aplicación de Android UltraSonic.</string> <string name="api.subsonic.upgrade_server">Versiones incompatibles. Por favor actualiza el servidor de Subsonic.</string> + <string name="feature_flags_category_title">Banderas de características</string> + <string name="feature_flags_image_loader_description">Permite la implementación de un nuevo cargador de imágenes. + Actualmente no guarda la imagen en el almacenamiento del dispositivo y sólo utiliza caché en la memoria. + </string> + <string name="feature_flags_image_loader_title">Habilitar nuevo cargador de imágenes</string> </resources> diff --git a/ultrasonic/src/main/res/values-fr/strings.xml b/ultrasonic/src/main/res/values-fr/strings.xml index f2ca90fc..ddd81d03 100644 --- a/ultrasonic/src/main/res/values-fr/strings.xml +++ b/ultrasonic/src/main/res/values-fr/strings.xml @@ -436,5 +436,11 @@ <string name="api.subsonic.trial_period_is_over">La période d\'essai est terminée.</string> <string name="api.subsonic.upgrade_client">Versions incompatible. Veuillez mette à jour l\'application Android UltraSonic.</string> <string name="api.subsonic.upgrade_server">Versions incompatible. Veuillez mette à jour le serveur Subsonic.</string> + <string name="feature_flags_category_title">Drapeaux des fonctionnalités</string> + <string name="feature_flags_image_loader_description">Permet l\'implémentation d\'un nouveau chargeur d\'images. + Actuellement, il n\'enregistre pas l\'image dans le stockage de l\'appareil et n\'utilise que le cache en + mémoire. + </string> + <string name="feature_flags_image_loader_title">Activer le nouveau chargeur d\'images</string> </resources> diff --git a/ultrasonic/src/main/res/values-hu/strings.xml b/ultrasonic/src/main/res/values-hu/strings.xml index 9bfe728e..bca57a9c 100644 --- a/ultrasonic/src/main/res/values-hu/strings.xml +++ b/ultrasonic/src/main/res/values-hu/strings.xml @@ -436,5 +436,10 @@ <string name="api.subsonic.trial_period_is_over">A próbaidő vége.</string> <string name="api.subsonic.upgrade_client">Nem kompatibilis verzió. Kérjük, frissítse az UltraSonic Android alkalmazást!</string> <string name="api.subsonic.upgrade_server">Nem kompatibilis verzió. Kérjük, frissítse a Subsonic kiszolgálót!</string> + <string name="feature_flags_image_loader_description">Engedélyezi az új képbetöltő megvalósítását. Jelenleg nem + tárolja a képet az eszköz tárolójában, és csak a memóriában tárolja a gyorsítótárat. + </string> + <string name="feature_flags_category_title">Jellemzők Zászlók</string> + <string name="feature_flags_image_loader_title">Engedélyezzen új képbetöltőt</string> </resources> diff --git a/ultrasonic/src/main/res/values-pt-rBR/strings.xml b/ultrasonic/src/main/res/values-pt-rBR/strings.xml index a6085eda..4c0c6dfa 100644 --- a/ultrasonic/src/main/res/values-pt-rBR/strings.xml +++ b/ultrasonic/src/main/res/values-pt-rBR/strings.xml @@ -436,5 +436,10 @@ <string name="api.subsonic.trial_period_is_over">O período de avaliação acabou.</string> <string name="api.subsonic.upgrade_client">Versões incompativeis. Atualize o aplicativo UltraSonic para Android.</string> <string name="api.subsonic.upgrade_server">Versões incompativeis. Atualize o servidor UltraSonic.</string> + <string name="feature_flags_image_loader_description">Permite nova implementação do carregador de imagens. + Atualmente, ele não salva a imagem no armazenamento do dispositivo e usa apenas o cache na memória. + </string> + <string name="feature_flags_category_title">Bandeiras de recursos</string> + <string name="feature_flags_image_loader_title">Ativar novo carregador de imagens</string> </resources> diff --git a/ultrasonic/src/main/res/values-pt/strings.xml b/ultrasonic/src/main/res/values-pt/strings.xml index d5f1fcaf..784e116f 100644 --- a/ultrasonic/src/main/res/values-pt/strings.xml +++ b/ultrasonic/src/main/res/values-pt/strings.xml @@ -436,5 +436,10 @@ <string name="api.subsonic.trial_period_is_over">O período de avaliação acabou.</string> <string name="api.subsonic.upgrade_client">Versões incompativeis. Atualize o aplicativo UltraSonic para Android.</string> <string name="api.subsonic.upgrade_server">Versões incompativeis. Atualize o servidor UltraSonic.</string> + <string name="feature_flags_image_loader_description">Permite nova implementação do carregador de imagens. + Atualmente, ele não salva a imagem no armazenamento do dispositivo e usa apenas o cache na memória. + </string> + <string name="feature_flags_category_title">Bandeiras de recursos</string> + <string name="feature_flags_image_loader_title">Ativar novo carregador de imagens</string> </resources> diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index b9edf0d0..fe80ab0c 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -441,4 +441,10 @@ <string name="api.subsonic.upgrade_client">Incompatible versions. Please upgrade UltraSonic Android app.</string> <string name="api.subsonic.upgrade_server">Incompatible versions. Please upgrade Subsonic server.</string> + <string name="feature_flags_image_loader_title">Enable new image loader</string> + <string name="feature_flags_image_loader_description">Enables new image loader implementation. + Currently it doesn\'t save image in device storage and uses only cache in memory. + </string> + <string name="feature_flags_category_title">Feature Flags</string> + </resources> diff --git a/ultrasonic/src/main/res/xml/settings.xml b/ultrasonic/src/main/res/xml/settings.xml index e66f2b7e..a4a52386 100644 --- a/ultrasonic/src/main/res/xml/settings.xml +++ b/ultrasonic/src/main/res/xml/settings.xml @@ -284,5 +284,14 @@ a:summary="@string/settings.screen_lit_summary" a:title="@string/settings.screen_lit_title"/> </PreferenceCategory> + <PreferenceCategory a:title="@string/feature_flags_category_title"> + <CheckBoxPreference + a:key="ff_new_image_loader" + a:persistent="false" + a:title="@string/feature_flags_image_loader_title" + a:summary="@string/feature_flags_image_loader_description" + /> + + </PreferenceCategory> </PreferenceScreen> \ No newline at end of file