Add feature flags that contains new image loader flag.

This allows to release new versions of the app with unfinished/
not fully implemented features.

Signed-off-by: Yahor Berdnikau <egorr.berd@gmail.com>
This commit is contained in:
Yahor Berdnikau 2018-07-14 22:14:20 +02:00
parent 02467cb05b
commit a63c47112c
14 changed files with 153 additions and 21 deletions

View File

@ -44,6 +44,7 @@ import org.moire.ultrasonic.domain.MusicDirectory;
import org.moire.ultrasonic.domain.MusicDirectory.Entry; import org.moire.ultrasonic.domain.MusicDirectory.Entry;
import org.moire.ultrasonic.domain.PlayerState; import org.moire.ultrasonic.domain.PlayerState;
import org.moire.ultrasonic.domain.Share; import org.moire.ultrasonic.domain.Share;
import org.moire.ultrasonic.featureflags.Feature;
import org.moire.ultrasonic.service.*; import org.moire.ultrasonic.service.*;
import org.moire.ultrasonic.subsonic.SubsonicImageLoaderProxy; import org.moire.ultrasonic.subsonic.SubsonicImageLoaderProxy;
import org.moire.ultrasonic.util.*; import org.moire.ultrasonic.util.*;
@ -790,10 +791,14 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
} }
} }
public synchronized void clearImageLoader() public synchronized void clearImageLoader() {
{ if (IMAGE_LOADER != null &&
if (IMAGE_LOADER != null && IMAGE_LOADER.isRunning()) IMAGE_LOADER.clear(); IMAGE_LOADER.isRunning()) {
} IMAGE_LOADER.clear();
}
IMAGE_LOADER = null;
}
public synchronized ImageLoader getImageLoader() { public synchronized ImageLoader getImageLoader() {
if (IMAGE_LOADER == null || if (IMAGE_LOADER == null ||
@ -802,10 +807,18 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
this, this,
Util.getImageLoaderConcurrency(this) Util.getImageLoaderConcurrency(this)
); );
IMAGE_LOADER = new SubsonicImageLoaderProxy(
legacyImageLoader, boolean isNewImageLoaderEnabled = ((UApp) getApplication()).getFeaturesStorage()
((UApp) getApplication()).getSubsonicImageLoader() .isFeatureEnabled(Feature.NEW_IMAGE_DOWNLOADER);
); if (isNewImageLoaderEnabled) {
IMAGE_LOADER = new SubsonicImageLoaderProxy(
legacyImageLoader,
((UApp) getApplication()).getSubsonicImageLoader()
);
} else {
IMAGE_LOADER = legacyImageLoader;
}
IMAGE_LOADER.startImageLoader(); IMAGE_LOADER.startImageLoader();
} }

View File

@ -4,29 +4,21 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.preference.CheckBoxPreference; import android.preference.*;
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.provider.SearchRecentSuggestions; import android.provider.SearchRecentSuggestions;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import org.moire.ultrasonic.R; import org.moire.ultrasonic.R;
import org.moire.ultrasonic.activity.ServerSettingsActivity; import org.moire.ultrasonic.activity.ServerSettingsActivity;
import org.moire.ultrasonic.activity.SubsonicTabActivity; 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.provider.SearchSuggestionProvider;
import org.moire.ultrasonic.service.DownloadService; import org.moire.ultrasonic.service.DownloadService;
import org.moire.ultrasonic.service.DownloadServiceImpl; import org.moire.ultrasonic.service.DownloadServiceImpl;
import org.moire.ultrasonic.util.Constants; import org.moire.ultrasonic.util.*;
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 java.io.File; import java.io.File;
@ -115,6 +107,7 @@ public class SettingsFragment extends PreferenceFragment
sharingDefaultGreeting.setText(Util.getShareGreeting(getActivity())); sharingDefaultGreeting.setText(Util.getShareGreeting(getActivity()));
setupClearSearchPreference(); setupClearSearchPreference();
setupGaplessControlSettingsV14(); setupGaplessControlSettingsV14();
setupFeatureFlagsPreferences();
} }
@Override @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() { private void setupGaplessControlSettingsV14() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
PreferenceCategory playbackControlSettings = PreferenceCategory playbackControlSettings =

View File

@ -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_SHOW_ALL_SONGS_BY_ARTIST = "showAllSongsByArtist";
public static final String PREFERENCES_KEY_SCAN_MEDIA = "scanMedia"; 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_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. // Number of free trial days for non-licensed servers.
public static final int FREE_TRIAL_DAYS = 30; public static final int FREE_TRIAL_DAYS = 30;

View File

@ -5,7 +5,9 @@ import org.koin.android.ext.android.get
import org.koin.android.ext.android.startKoin import org.koin.android.ext.android.startKoin
import org.moire.ultrasonic.di.baseNetworkModule import org.moire.ultrasonic.di.baseNetworkModule
import org.moire.ultrasonic.di.directoriesModule import org.moire.ultrasonic.di.directoriesModule
import org.moire.ultrasonic.di.featureFlagsModule
import org.moire.ultrasonic.di.musicServiceModule import org.moire.ultrasonic.di.musicServiceModule
import org.moire.ultrasonic.featureflags.FeatureStorage
import org.moire.ultrasonic.subsonic.loader.image.SubsonicImageLoader import org.moire.ultrasonic.subsonic.loader.image.SubsonicImageLoader
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
@ -17,6 +19,7 @@ class UApp : Application() {
startKoin(this, listOf( startKoin(this, listOf(
directoriesModule, directoriesModule,
baseNetworkModule, baseNetworkModule,
featureFlagsModule(this),
musicServiceModule(sharedPreferences, this) musicServiceModule(sharedPreferences, this)
)) ))
} }
@ -27,4 +30,11 @@ class UApp : Application() {
fun getSubsonicImageLoader(): SubsonicImageLoader { fun getSubsonicImageLoader(): SubsonicImageLoader {
return get() return get()
} }
/**
* Temporary method to get features storage.
*/
fun getFeaturesStorage(): FeatureStorage {
return get()
}
} }

View File

@ -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) }
}

View File

@ -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)
}

View File

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

View File

@ -436,5 +436,10 @@
<string name="api.subsonic.trial_period_is_over">El período de prueba ha terminado.</string> <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_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="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> </resources>

View File

@ -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.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_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="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> </resources>

View File

@ -436,5 +436,10 @@
<string name="api.subsonic.trial_period_is_over">A próbaidő vége.</string> <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_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="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> </resources>

View File

@ -436,5 +436,10 @@
<string name="api.subsonic.trial_period_is_over">O período de avaliação acabou.</string> <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_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="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> </resources>

View File

@ -436,5 +436,10 @@
<string name="api.subsonic.trial_period_is_over">O período de avaliação acabou.</string> <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_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="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> </resources>

View File

@ -441,4 +441,10 @@
<string name="api.subsonic.upgrade_client">Incompatible versions. Please upgrade UltraSonic Android app.</string> <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="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> </resources>

View File

@ -284,5 +284,14 @@
a:summary="@string/settings.screen_lit_summary" a:summary="@string/settings.screen_lit_summary"
a:title="@string/settings.screen_lit_title"/> a:title="@string/settings.screen_lit_title"/>
</PreferenceCategory> </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> </PreferenceScreen>