Remove LegacyImageLoader, use Picasso as default
This commit is contained in:
parent
2eaa9a2091
commit
0989ee20a6
|
@ -14,19 +14,19 @@ internal const val QUERY_USERNAME = "username"
|
||||||
* Picasso.load() only accepts an URI as parameter. Therefore we create a bogus URI, in which
|
* Picasso.load() only accepts an URI as parameter. Therefore we create a bogus URI, in which
|
||||||
* we encode the data that we need in the RequestHandler.
|
* we encode the data that we need in the RequestHandler.
|
||||||
*/
|
*/
|
||||||
internal fun createLoadCoverArtRequest(config: ImageRequest.CoverArt): Uri =
|
internal fun createLoadCoverArtRequest(entityId: String, size: Long? = 0): Uri =
|
||||||
Uri.Builder()
|
Uri.Builder()
|
||||||
.scheme(SCHEME)
|
.scheme(SCHEME)
|
||||||
.authority(AUTHORITY)
|
.authority(AUTHORITY)
|
||||||
.appendPath(COVER_ART_PATH)
|
.appendPath(COVER_ART_PATH)
|
||||||
.appendQueryParameter(QUERY_ID, config.entityId)
|
.appendQueryParameter(QUERY_ID, entityId)
|
||||||
.appendQueryParameter(SIZE, config.size.toString())
|
.appendQueryParameter(SIZE, size.toString())
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
internal fun createLoadAvatarRequest(username: String): Uri =
|
internal fun createLoadAvatarRequest(username: String): Uri =
|
||||||
Uri.Builder()
|
Uri.Builder()
|
||||||
.scheme(SCHEME)
|
.scheme(SCHEME)
|
||||||
.authority(AUTHORITY)
|
.authority(AUTHORITY)
|
||||||
.appendPath(AVATAR_PATH)
|
.appendPath(AVATAR_PATH)
|
||||||
.appendQueryParameter(QUERY_USERNAME, username)
|
.appendQueryParameter(QUERY_USERNAME, username)
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -6,9 +6,7 @@ import com.squareup.picasso.Picasso
|
||||||
import com.squareup.picasso.RequestCreator
|
import com.squareup.picasso.RequestCreator
|
||||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
||||||
|
|
||||||
// TODO: Caching doesn't work as expected because our query string varies.
|
// TODO: Implement OkHTTP disk caching
|
||||||
// Need to use .stableKey() method
|
|
||||||
|
|
||||||
class SubsonicImageLoader(
|
class SubsonicImageLoader(
|
||||||
context: Context,
|
context: Context,
|
||||||
apiClient: SubsonicAPIClient
|
apiClient: SubsonicAPIClient
|
||||||
|
@ -16,7 +14,10 @@ class SubsonicImageLoader(
|
||||||
private val picasso = Picasso.Builder(context)
|
private val picasso = Picasso.Builder(context)
|
||||||
.addRequestHandler(CoverArtRequestHandler(apiClient))
|
.addRequestHandler(CoverArtRequestHandler(apiClient))
|
||||||
.addRequestHandler(AvatarRequestHandler(apiClient))
|
.addRequestHandler(AvatarRequestHandler(apiClient))
|
||||||
.build().apply { setIndicatorsEnabled(true) }
|
.build().apply {
|
||||||
|
setIndicatorsEnabled(BuildConfig.DEBUG)
|
||||||
|
Picasso.setSingletonInstance(this)
|
||||||
|
}
|
||||||
|
|
||||||
fun load(request: ImageRequest) = when (request) {
|
fun load(request: ImageRequest) = when (request) {
|
||||||
is ImageRequest.CoverArt -> loadCoverArt(request)
|
is ImageRequest.CoverArt -> loadCoverArt(request)
|
||||||
|
@ -24,10 +25,10 @@ class SubsonicImageLoader(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadCoverArt(request: ImageRequest.CoverArt) {
|
private fun loadCoverArt(request: ImageRequest.CoverArt) {
|
||||||
picasso.load(createLoadCoverArtRequest(request))
|
picasso.load(createLoadCoverArtRequest(request.entityId, request.size.toLong()))
|
||||||
.addPlaceholder(request)
|
.addPlaceholder(request)
|
||||||
.addError(request)
|
.addError(request)
|
||||||
.stableKey("${request.entityId}-${request.size}" )
|
.stableKey("${request.entityId}-${request.size}")
|
||||||
.into(request.imageView)
|
.into(request.imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,7 @@ class SubsonicAPIClient(
|
||||||
val api: SubsonicAPIDefinition get() = wrappedApi
|
val api: SubsonicAPIDefinition get() = wrappedApi
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* TODO: Remove this in favour of handling the stream response inside RESTService
|
||||||
* Convenient method to get cover art from api using item [id] and optional maximum [size].
|
* Convenient method to get cover art from api using item [id] and optional maximum [size].
|
||||||
*
|
*
|
||||||
* It detects the response `Content-Type` and tries to parse subsonic error if there is one.
|
* It detects the response `Content-Type` and tries to parse subsonic error if there is one.
|
||||||
|
@ -114,6 +115,7 @@ class SubsonicAPIClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* TODO: Remove this in favour of handling the stream response inside RESTService
|
||||||
* Convenient method to get media stream from api using item [id] and optional [maxBitrate].
|
* Convenient method to get media stream from api using item [id] and optional [maxBitrate].
|
||||||
*
|
*
|
||||||
* Optionally also you can provide [offset] that stream should start from.
|
* Optionally also you can provide [offset] that stream should start from.
|
||||||
|
@ -128,6 +130,7 @@ class SubsonicAPIClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* TODO: Remove this in favour of handling the stream response inside RESTService
|
||||||
* Convenient method to get user avatar using [username].
|
* Convenient method to get user avatar using [username].
|
||||||
*
|
*
|
||||||
* It detects the response `Content-Type` and tries to parse subsonic error if there is one.
|
* It detects the response `Content-Type` and tries to parse subsonic error if there is one.
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
package org.moire.ultrasonic.api.subsonic.di
|
|
||||||
|
|
||||||
import org.koin.dsl.module
|
|
||||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
|
||||||
|
|
||||||
val subsonicApiModule = module {
|
|
||||||
single { SubsonicAPIClient(get(), get()) }
|
|
||||||
}
|
|
|
@ -6,6 +6,8 @@ import android.content.SharedPreferences;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.SearchRecentSuggestions;
|
import android.provider.SearchRecentSuggestions;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
@ -18,9 +20,6 @@ import androidx.preference.PreferenceCategory;
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import timber.log.Timber;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.koin.java.KoinJavaComponent;
|
import org.koin.java.KoinJavaComponent;
|
||||||
import org.moire.ultrasonic.R;
|
import org.moire.ultrasonic.R;
|
||||||
|
@ -32,12 +31,18 @@ import org.moire.ultrasonic.log.FileLoggerTree;
|
||||||
import org.moire.ultrasonic.provider.SearchSuggestionProvider;
|
import org.moire.ultrasonic.provider.SearchSuggestionProvider;
|
||||||
import org.moire.ultrasonic.service.Consumer;
|
import org.moire.ultrasonic.service.Consumer;
|
||||||
import org.moire.ultrasonic.service.MediaPlayerController;
|
import org.moire.ultrasonic.service.MediaPlayerController;
|
||||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider;
|
import org.moire.ultrasonic.util.Constants;
|
||||||
import org.moire.ultrasonic.util.*;
|
import org.moire.ultrasonic.util.FileUtil;
|
||||||
|
import org.moire.ultrasonic.util.PermissionUtil;
|
||||||
|
import org.moire.ultrasonic.util.ThemeChangedEventDistributor;
|
||||||
|
import org.moire.ultrasonic.util.TimeSpanPreference;
|
||||||
|
import org.moire.ultrasonic.util.TimeSpanPreferenceDialogFragmentCompat;
|
||||||
|
import org.moire.ultrasonic.util.Util;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import kotlin.Lazy;
|
import kotlin.Lazy;
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static org.koin.java.KoinJavaComponent.inject;
|
import static org.koin.java.KoinJavaComponent.inject;
|
||||||
import static org.moire.ultrasonic.fragment.ServerSelectorFragment.SERVER_SELECTOR_MANAGE_MODE;
|
import static org.moire.ultrasonic.fragment.ServerSelectorFragment.SERVER_SELECTOR_MANAGE_MODE;
|
||||||
|
@ -73,7 +78,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||||
private CheckBoxPreference sendBluetoothAlbumArt;
|
private CheckBoxPreference sendBluetoothAlbumArt;
|
||||||
private CheckBoxPreference showArtistPicture;
|
private CheckBoxPreference showArtistPicture;
|
||||||
private ListPreference viewRefresh;
|
private ListPreference viewRefresh;
|
||||||
private ListPreference imageLoaderConcurrency;
|
|
||||||
private EditTextPreference sharingDefaultDescription;
|
private EditTextPreference sharingDefaultDescription;
|
||||||
private EditTextPreference sharingDefaultGreeting;
|
private EditTextPreference sharingDefaultGreeting;
|
||||||
private TimeSpanPreference sharingDefaultExpiration;
|
private TimeSpanPreference sharingDefaultExpiration;
|
||||||
|
@ -84,7 +88,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||||
private SharedPreferences settings;
|
private SharedPreferences settings;
|
||||||
|
|
||||||
private final Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
|
private final Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
|
||||||
private final Lazy<ImageLoaderProvider> imageLoader = inject(ImageLoaderProvider.class);
|
|
||||||
private final Lazy<PermissionUtil> permissionUtil = inject(PermissionUtil.class);
|
private final Lazy<PermissionUtil> permissionUtil = inject(PermissionUtil.class);
|
||||||
private final Lazy<ThemeChangedEventDistributor> themeChangedEventDistributor = inject(ThemeChangedEventDistributor.class);
|
private final Lazy<ThemeChangedEventDistributor> themeChangedEventDistributor = inject(ThemeChangedEventDistributor.class);
|
||||||
|
|
||||||
|
@ -129,7 +132,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||||
sendBluetoothAlbumArt = findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_ALBUM_ART);
|
sendBluetoothAlbumArt = findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_ALBUM_ART);
|
||||||
sendBluetoothNotifications = findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_NOTIFICATIONS);
|
sendBluetoothNotifications = findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_NOTIFICATIONS);
|
||||||
viewRefresh = findPreference(Constants.PREFERENCES_KEY_VIEW_REFRESH);
|
viewRefresh = findPreference(Constants.PREFERENCES_KEY_VIEW_REFRESH);
|
||||||
imageLoaderConcurrency = findPreference(Constants.PREFERENCES_KEY_IMAGE_LOADER_CONCURRENCY);
|
|
||||||
sharingDefaultDescription = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION);
|
sharingDefaultDescription = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION);
|
||||||
sharingDefaultGreeting = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_GREETING);
|
sharingDefaultGreeting = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_GREETING);
|
||||||
sharingDefaultExpiration = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION);
|
sharingDefaultExpiration = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION);
|
||||||
|
@ -188,8 +190,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||||
setMediaButtonsEnabled(sharedPreferences.getBoolean(key, true));
|
setMediaButtonsEnabled(sharedPreferences.getBoolean(key, true));
|
||||||
} else if (Constants.PREFERENCES_KEY_SEND_BLUETOOTH_NOTIFICATIONS.equals(key)) {
|
} else if (Constants.PREFERENCES_KEY_SEND_BLUETOOTH_NOTIFICATIONS.equals(key)) {
|
||||||
setBluetoothPreferences(sharedPreferences.getBoolean(key, true));
|
setBluetoothPreferences(sharedPreferences.getBoolean(key, true));
|
||||||
} else if (Constants.PREFERENCES_KEY_IMAGE_LOADER_CONCURRENCY.equals(key)) {
|
|
||||||
setImageLoaderConcurrency(Integer.parseInt(sharedPreferences.getString(key, "5")));
|
|
||||||
} else if (Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE.equals(key)) {
|
} else if (Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE.equals(key)) {
|
||||||
setDebugLogToFile(sharedPreferences.getBoolean(key, false));
|
setDebugLogToFile(sharedPreferences.getBoolean(key, false));
|
||||||
} else if (Constants.PREFERENCES_KEY_ID3_TAGS.equals(key)) {
|
} else if (Constants.PREFERENCES_KEY_ID3_TAGS.equals(key)) {
|
||||||
|
@ -359,21 +359,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||||
private void setupFeatureFlagsPreferences() {
|
private void setupFeatureFlagsPreferences() {
|
||||||
final FeatureStorage featureStorage = KoinJavaComponent.get(FeatureStorage.class);
|
final FeatureStorage featureStorage = KoinJavaComponent.get(FeatureStorage.class);
|
||||||
|
|
||||||
CheckBoxPreference ffImageLoader = (CheckBoxPreference) findPreference(
|
|
||||||
Constants.PREFERENCES_KEY_FF_IMAGE_LOADER);
|
|
||||||
|
|
||||||
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);
|
|
||||||
imageLoader.getValue().clearImageLoader();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckBoxPreference useFiveStarRating = (CheckBoxPreference) findPreference(
|
CheckBoxPreference useFiveStarRating = (CheckBoxPreference) findPreference(
|
||||||
Constants.PREFERENCES_KEY_USE_FIVE_STAR_RATING);
|
Constants.PREFERENCES_KEY_USE_FIVE_STAR_RATING);
|
||||||
|
|
||||||
|
@ -443,7 +428,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||||
chatRefreshInterval.setSummary(chatRefreshInterval.getEntry());
|
chatRefreshInterval.setSummary(chatRefreshInterval.getEntry());
|
||||||
directoryCacheTime.setSummary(directoryCacheTime.getEntry());
|
directoryCacheTime.setSummary(directoryCacheTime.getEntry());
|
||||||
viewRefresh.setSummary(viewRefresh.getEntry());
|
viewRefresh.setSummary(viewRefresh.getEntry());
|
||||||
imageLoaderConcurrency.setSummary(imageLoaderConcurrency.getEntry());
|
|
||||||
sharingDefaultExpiration.setSummary(sharingDefaultExpiration.getText());
|
sharingDefaultExpiration.setSummary(sharingDefaultExpiration.getText());
|
||||||
sharingDefaultDescription.setSummary(sharingDefaultDescription.getText());
|
sharingDefaultDescription.setSummary(sharingDefaultDescription.getText());
|
||||||
sharingDefaultGreeting.setSummary(sharingDefaultGreeting.getText());
|
sharingDefaultGreeting.setSummary(sharingDefaultGreeting.getText());
|
||||||
|
@ -470,14 +454,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||||
showArtistPicture.setEnabled(Util.getShouldUseId3Tags());
|
showArtistPicture.setEnabled(Util.getShouldUseId3Tags());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setImageLoaderConcurrency(int concurrency) {
|
|
||||||
ImageLoader imageLoaderInstance = imageLoader.getValue().getImageLoader();
|
|
||||||
|
|
||||||
if (imageLoaderInstance != null) {
|
|
||||||
imageLoaderInstance.stopImageLoader();
|
|
||||||
imageLoaderInstance.setConcurrency(concurrency);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setHideMedia(boolean hide) {
|
private void setHideMedia(boolean hide) {
|
||||||
File nomediaDir = new File(FileUtil.getUltrasonicDirectory(), ".nomedia");
|
File nomediaDir = new File(FileUtil.getUltrasonicDirectory(), ".nomedia");
|
||||||
|
|
|
@ -9,7 +9,6 @@ import android.content.Intent;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import timber.log.Timber;
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
|
@ -21,6 +20,8 @@ import org.moire.ultrasonic.service.MediaPlayerController;
|
||||||
import org.moire.ultrasonic.util.Constants;
|
import org.moire.ultrasonic.util.Constants;
|
||||||
import org.moire.ultrasonic.util.FileUtil;
|
import org.moire.ultrasonic.util.FileUtil;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Widget Provider for the Ultrasonic Widgets
|
* Widget Provider for the Ultrasonic Widgets
|
||||||
*/
|
*/
|
||||||
|
@ -159,7 +160,7 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider
|
||||||
// Set the cover art
|
// Set the cover art
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Bitmap bitmap = currentSong == null ? null : FileUtil.getAlbumArtBitmap(currentSong, 240, true);
|
Bitmap bitmap = currentSong == null ? null : FileUtil.getAlbumArtBitmapFromDisk(currentSong, 240, true);
|
||||||
|
|
||||||
if (bitmap == null)
|
if (bitmap == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,14 +23,11 @@ import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
import android.os.StatFs;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import kotlin.Lazy;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import org.moire.ultrasonic.app.UApp;
|
import org.moire.ultrasonic.app.UApp;
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
@ -60,7 +57,6 @@ public class FileUtil
|
||||||
private static final List<String> PLAYLIST_FILE_EXTENSIONS = Collections.singletonList("m3u");
|
private static final List<String> PLAYLIST_FILE_EXTENSIONS = Collections.singletonList("m3u");
|
||||||
private static final Pattern TITLE_WITH_TRACK = Pattern.compile("^\\d\\d-.*");
|
private static final Pattern TITLE_WITH_TRACK = Pattern.compile("^\\d\\d-.*");
|
||||||
|
|
||||||
private static final Lazy<ImageLoaderProvider> imageLoaderProvider = inject(ImageLoaderProvider.class);
|
|
||||||
private static final Lazy<PermissionUtil> permissionUtil = inject(PermissionUtil.class);
|
private static final Lazy<PermissionUtil> permissionUtil = inject(PermissionUtil.class);
|
||||||
|
|
||||||
public static File getSongFile(MusicDirectory.Entry song)
|
public static File getSongFile(MusicDirectory.Entry song)
|
||||||
|
@ -118,6 +114,11 @@ public class FileUtil
|
||||||
return playlistDir;
|
return playlistDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the album art file for a given album entry
|
||||||
|
* @param entry The album entry
|
||||||
|
* @return File object. Not guaranteed that it exists
|
||||||
|
*/
|
||||||
public static File getAlbumArtFile(MusicDirectory.Entry entry)
|
public static File getAlbumArtFile(MusicDirectory.Entry entry)
|
||||||
{
|
{
|
||||||
File albumDir = getAlbumDirectory(entry);
|
File albumDir = getAlbumDirectory(entry);
|
||||||
|
@ -137,6 +138,11 @@ public class FileUtil
|
||||||
return new File(albumArtDir, String.format("%s.jpeg", md5Hex));
|
return new File(albumArtDir, String.format("%s.jpeg", md5Hex));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the album art file for a given album directory
|
||||||
|
* @param albumDir The album directory
|
||||||
|
* @return File object. Not guaranteed that it exists
|
||||||
|
*/
|
||||||
public static File getAlbumArtFile(File albumDir)
|
public static File getAlbumArtFile(File albumDir)
|
||||||
{
|
{
|
||||||
File albumArtDir = getAlbumArtDirectory();
|
File albumArtDir = getAlbumArtDirectory();
|
||||||
|
@ -150,24 +156,13 @@ public class FileUtil
|
||||||
return new File(albumArtDir, String.format("%s.jpeg", md5Hex));
|
return new File(albumArtDir, String.format("%s.jpeg", md5Hex));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap getAvatarBitmap(String username, int size, boolean highQuality)
|
public static Bitmap getAvatarBitmapFromDisk(String username, int size, boolean highQuality)
|
||||||
{
|
{
|
||||||
if (username == null) return null;
|
if (username == null) return null;
|
||||||
|
|
||||||
File avatarFile = getAvatarFile(username);
|
File avatarFile = getAvatarFile(username);
|
||||||
|
|
||||||
Bitmap bitmap = null;
|
Bitmap bitmap = null;
|
||||||
ImageLoader imageLoader = imageLoaderProvider.getValue().getImageLoader();
|
|
||||||
|
|
||||||
if (imageLoader != null)
|
|
||||||
{
|
|
||||||
bitmap = imageLoader.getImageBitmap(username, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bitmap != null)
|
|
||||||
{
|
|
||||||
return bitmap.copy(bitmap.getConfig(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avatarFile != null && avatarFile.exists())
|
if (avatarFile != null && avatarFile.exists())
|
||||||
{
|
{
|
||||||
|
@ -198,15 +193,7 @@ public class FileUtil
|
||||||
Timber.e(ex, "Exception in BitmapFactory.decodeFile()");
|
Timber.e(ex, "Exception in BitmapFactory.decodeFile()");
|
||||||
}
|
}
|
||||||
|
|
||||||
Timber.i("getAvatarBitmap %s", String.valueOf(size));
|
Timber.i("getAvatarBitmapFromDisk %s", String.valueOf(size));
|
||||||
|
|
||||||
if (bitmap != null)
|
|
||||||
{
|
|
||||||
if (imageLoader != null)
|
|
||||||
{
|
|
||||||
imageLoader.addImageToCache(bitmap, username, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
@ -214,24 +201,13 @@ public class FileUtil
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap getAlbumArtBitmap(MusicDirectory.Entry entry, int size, boolean highQuality)
|
public static Bitmap getAlbumArtBitmapFromDisk(MusicDirectory.Entry entry, int size, boolean highQuality)
|
||||||
{
|
{
|
||||||
if (entry == null) return null;
|
if (entry == null) return null;
|
||||||
|
|
||||||
File albumArtFile = getAlbumArtFile(entry);
|
File albumArtFile = getAlbumArtFile(entry);
|
||||||
|
|
||||||
Bitmap bitmap = null;
|
Bitmap bitmap = null;
|
||||||
ImageLoader imageLoader = imageLoaderProvider.getValue().getImageLoader();
|
|
||||||
|
|
||||||
if (imageLoader != null)
|
|
||||||
{
|
|
||||||
bitmap = imageLoader.getImageBitmap(entry, true, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bitmap != null)
|
|
||||||
{
|
|
||||||
return bitmap.copy(bitmap.getConfig(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (albumArtFile != null && albumArtFile.exists())
|
if (albumArtFile != null && albumArtFile.exists())
|
||||||
{
|
{
|
||||||
|
@ -262,15 +238,7 @@ public class FileUtil
|
||||||
Timber.e(ex, "Exception in BitmapFactory.decodeFile()");
|
Timber.e(ex, "Exception in BitmapFactory.decodeFile()");
|
||||||
}
|
}
|
||||||
|
|
||||||
Timber.i("getAlbumArtBitmap %s", String.valueOf(size));
|
Timber.i("getAlbumArtBitmapFromDisk %s", String.valueOf(size));
|
||||||
|
|
||||||
if (bitmap != null)
|
|
||||||
{
|
|
||||||
if (imageLoader != null)
|
|
||||||
{
|
|
||||||
imageLoader.addImageToCache(bitmap, entry, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,10 @@
|
||||||
package org.moire.ultrasonic.util;
|
package org.moire.ultrasonic.util;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||||
|
|
||||||
public interface ImageLoader {
|
public interface ImageLoader {
|
||||||
boolean isRunning();
|
|
||||||
|
|
||||||
void setConcurrency(int concurrency);
|
|
||||||
|
|
||||||
void startImageLoader();
|
|
||||||
|
|
||||||
void stopImageLoader();
|
|
||||||
|
|
||||||
void loadAvatarImage(View view, String username, boolean large, int size, boolean crossFade,
|
void loadAvatarImage(View view, String username, boolean large, int size, boolean crossFade,
|
||||||
boolean highQuality);
|
boolean highQuality);
|
||||||
|
|
||||||
|
@ -22,15 +14,4 @@ public interface ImageLoader {
|
||||||
void loadImage(View view, MusicDirectory.Entry entry, boolean large, int size,
|
void loadImage(View view, MusicDirectory.Entry entry, boolean large, int size,
|
||||||
boolean crossFade, boolean highQuality, int defaultResourceId);
|
boolean crossFade, boolean highQuality, int defaultResourceId);
|
||||||
|
|
||||||
void cancel(String coverArt);
|
|
||||||
|
|
||||||
Bitmap getImageBitmap(String username, int size);
|
|
||||||
|
|
||||||
Bitmap getImageBitmap(MusicDirectory.Entry entry, boolean large, int size);
|
|
||||||
|
|
||||||
void addImageToCache(Bitmap bitmap, MusicDirectory.Entry entry, int size);
|
|
||||||
|
|
||||||
void addImageToCache(Bitmap bitmap, String username, int size);
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,450 +0,0 @@
|
||||||
/*
|
|
||||||
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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
Copyright 2009 (C) Sindre Mehus
|
|
||||||
*/
|
|
||||||
package org.moire.ultrasonic.util;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.graphics.drawable.TransitionDrawable;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import timber.log.Timber;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.core.content.res.ResourcesCompat;
|
|
||||||
|
|
||||||
import org.moire.ultrasonic.R;
|
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
|
||||||
import org.moire.ultrasonic.service.MusicService;
|
|
||||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronous loading of images, with caching.
|
|
||||||
* <p/>
|
|
||||||
* There should normally be only one instance of this class.
|
|
||||||
*
|
|
||||||
* @author Sindre Mehus
|
|
||||||
*/
|
|
||||||
public class LegacyImageLoader implements Runnable, ImageLoader {
|
|
||||||
private final LRUCache<String, Bitmap> cache = new LRUCache<>(150);
|
|
||||||
private final BlockingQueue<Task> queue;
|
|
||||||
private int imageSizeDefault;
|
|
||||||
private final int imageSizeLarge;
|
|
||||||
private Bitmap largeUnknownImage;
|
|
||||||
private Bitmap unknownAvatarImage;
|
|
||||||
private final Context context;
|
|
||||||
private Collection<Thread> threads;
|
|
||||||
private final AtomicBoolean running = new AtomicBoolean();
|
|
||||||
private int concurrency;
|
|
||||||
|
|
||||||
public LegacyImageLoader(
|
|
||||||
Context context,
|
|
||||||
int concurrency
|
|
||||||
) {
|
|
||||||
this.context = context;
|
|
||||||
this.concurrency = concurrency;
|
|
||||||
queue = new LinkedBlockingQueue<>(1000);
|
|
||||||
|
|
||||||
Drawable drawable = ResourcesCompat.getDrawable(context.getResources(), R.drawable.unknown_album, null);
|
|
||||||
|
|
||||||
// Determine the density-dependent image sizes.
|
|
||||||
if (drawable != null) {
|
|
||||||
imageSizeDefault = drawable.getIntrinsicHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
imageSizeLarge = Util.getMaxDisplayMetric();
|
|
||||||
createLargeUnknownImage(context);
|
|
||||||
createUnknownAvatarImage(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized boolean isRunning() {
|
|
||||||
return running.get() && !threads.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setConcurrency(int concurrency) {
|
|
||||||
this.concurrency = concurrency;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startImageLoader() {
|
|
||||||
running.set(true);
|
|
||||||
|
|
||||||
threads = Collections.synchronizedCollection(new ArrayList<Thread>(this.concurrency));
|
|
||||||
|
|
||||||
for (int i = 0; i < this.concurrency; i++) {
|
|
||||||
Thread thread = new Thread(this, String.format(Locale.US, "ImageLoader_%d", i));
|
|
||||||
threads.add(thread);
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized void stopImageLoader() {
|
|
||||||
clear();
|
|
||||||
|
|
||||||
for (Thread thread : threads) {
|
|
||||||
thread.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
running.set(false);
|
|
||||||
threads.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createLargeUnknownImage(Context context) {
|
|
||||||
Drawable drawable = ResourcesCompat.getDrawable(context.getResources(), R.drawable.unknown_album, null);
|
|
||||||
Timber.i("createLargeUnknownImage");
|
|
||||||
|
|
||||||
if (drawable != null) {
|
|
||||||
largeUnknownImage = Util.createBitmapFromDrawable(drawable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createUnknownAvatarImage(Context context) {
|
|
||||||
Drawable contact = ResourcesCompat.getDrawable(context.getResources(), R.drawable.ic_contact_picture, null);
|
|
||||||
unknownAvatarImage = Util.createBitmapFromDrawable(contact);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadAvatarImage(
|
|
||||||
View view,
|
|
||||||
String username,
|
|
||||||
boolean large,
|
|
||||||
int size,
|
|
||||||
boolean crossFade,
|
|
||||||
boolean highQuality
|
|
||||||
) {
|
|
||||||
view.invalidate();
|
|
||||||
|
|
||||||
if (username == null) {
|
|
||||||
setUnknownAvatarImage(view);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size <= 0) {
|
|
||||||
size = large ? imageSizeLarge : imageSizeDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
Bitmap bitmap = cache.get(getKey(username, size));
|
|
||||||
|
|
||||||
if (bitmap != null) {
|
|
||||||
setAvatarImageBitmap(view, username, bitmap, crossFade);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setUnknownAvatarImage(view);
|
|
||||||
|
|
||||||
queue.offer(new Task(view, username, size, large, crossFade, highQuality));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadImage(View view, MusicDirectory.Entry entry, boolean large, int size,
|
|
||||||
boolean crossFade, boolean highQuality) {
|
|
||||||
loadImage(view, entry, large, size, crossFade, highQuality, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadImage(View view, MusicDirectory.Entry entry, boolean large, int size,
|
|
||||||
boolean crossFade, boolean highQuality, int defaultResourceId) {
|
|
||||||
view.invalidate();
|
|
||||||
|
|
||||||
if (entry == null) {
|
|
||||||
setUnknownImage(view, large, defaultResourceId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String coverArt = entry.getCoverArt();
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(coverArt)) {
|
|
||||||
setUnknownImage(view, large, defaultResourceId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size <= 0) {
|
|
||||||
size = large ? imageSizeLarge : imageSizeDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
Bitmap bitmap = cache.get(getKey(coverArt, size));
|
|
||||||
|
|
||||||
if (bitmap != null) {
|
|
||||||
setImageBitmap(view, entry, bitmap, crossFade);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setUnknownImage(view, large, defaultResourceId);
|
|
||||||
|
|
||||||
queue.offer(new Task(view, entry, size, large, crossFade, highQuality));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancel(String coverArt) {
|
|
||||||
for (Object taskObject : queue.toArray()) {
|
|
||||||
Task task = (Task)taskObject;
|
|
||||||
if ((task.entry.getCoverArt() != null) && (coverArt.compareTo(task.entry.getCoverArt()) == 0)) {
|
|
||||||
queue.remove(taskObject);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getKey(String coverArtId, int size) {
|
|
||||||
return String.format(Locale.US, "%s:%d", coverArtId, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Bitmap getImageBitmap(String username, int size) {
|
|
||||||
Bitmap bitmap = cache.get(getKey(username, size));
|
|
||||||
|
|
||||||
if (bitmap != null && !bitmap.isRecycled()) {
|
|
||||||
Bitmap.Config config = bitmap.getConfig();
|
|
||||||
return bitmap.copy(config, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Bitmap getImageBitmap(MusicDirectory.Entry entry, boolean large, int size) {
|
|
||||||
if (entry == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String coverArt = entry.getCoverArt();
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(coverArt)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size <= 0) {
|
|
||||||
size = large ? imageSizeLarge : imageSizeDefault;
|
|
||||||
}
|
|
||||||
|
|
||||||
Bitmap bitmap = cache.get(getKey(coverArt, size));
|
|
||||||
|
|
||||||
if (bitmap != null && !bitmap.isRecycled()) {
|
|
||||||
Bitmap.Config config = bitmap.getConfig();
|
|
||||||
return bitmap.copy(config, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setImageBitmap(
|
|
||||||
View view,
|
|
||||||
MusicDirectory.Entry entry,
|
|
||||||
Bitmap bitmap,
|
|
||||||
boolean crossFade
|
|
||||||
) {
|
|
||||||
if (view instanceof ImageView) {
|
|
||||||
ImageView imageView = (ImageView) view;
|
|
||||||
|
|
||||||
MusicDirectory.Entry tagEntry = (MusicDirectory.Entry) view.getTag();
|
|
||||||
|
|
||||||
// Only apply image to the view if the view is intended for this entry
|
|
||||||
if (entry != null && tagEntry != null && !entry.equals(tagEntry)) {
|
|
||||||
Timber.i("View is no longer valid, not setting ImageBitmap");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crossFade) {
|
|
||||||
Drawable existingDrawable = imageView.getDrawable();
|
|
||||||
Drawable newDrawable = Util.createDrawableFromBitmap(this.context, bitmap);
|
|
||||||
|
|
||||||
if (existingDrawable == null) {
|
|
||||||
Bitmap emptyImage = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
|
|
||||||
existingDrawable = new BitmapDrawable(context.getResources(), emptyImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
Drawable[] layers = new Drawable[]{existingDrawable, newDrawable};
|
|
||||||
|
|
||||||
TransitionDrawable transitionDrawable = new TransitionDrawable(layers);
|
|
||||||
imageView.setImageDrawable(transitionDrawable);
|
|
||||||
transitionDrawable.startTransition(250);
|
|
||||||
} else {
|
|
||||||
imageView.setImageBitmap(bitmap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setAvatarImageBitmap(
|
|
||||||
View view,
|
|
||||||
String username,
|
|
||||||
Bitmap bitmap,
|
|
||||||
boolean crossFade
|
|
||||||
) {
|
|
||||||
if (view instanceof ImageView) {
|
|
||||||
ImageView imageView = (ImageView) view;
|
|
||||||
|
|
||||||
String tagEntry = (String) view.getTag();
|
|
||||||
|
|
||||||
// Only apply image to the view if the view is intended for this entry
|
|
||||||
if (username != null &&
|
|
||||||
tagEntry != null &&
|
|
||||||
!username.equals(tagEntry)) {
|
|
||||||
Timber.i("View is no longer valid, not setting ImageBitmap");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crossFade) {
|
|
||||||
Drawable existingDrawable = imageView.getDrawable();
|
|
||||||
Drawable newDrawable = Util.createDrawableFromBitmap(this.context, bitmap);
|
|
||||||
|
|
||||||
if (existingDrawable == null) {
|
|
||||||
Bitmap emptyImage = Bitmap.createBitmap(
|
|
||||||
bitmap.getWidth(),
|
|
||||||
bitmap.getHeight(),
|
|
||||||
Bitmap.Config.ARGB_8888
|
|
||||||
);
|
|
||||||
existingDrawable = new BitmapDrawable(context.getResources(), emptyImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
Drawable[] layers = new Drawable[]{existingDrawable, newDrawable};
|
|
||||||
|
|
||||||
TransitionDrawable transitionDrawable = new TransitionDrawable(layers);
|
|
||||||
imageView.setImageDrawable(transitionDrawable);
|
|
||||||
transitionDrawable.startTransition(250);
|
|
||||||
} else {
|
|
||||||
imageView.setImageBitmap(bitmap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setUnknownAvatarImage(View view) {
|
|
||||||
setAvatarImageBitmap(view, null, unknownAvatarImage, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setUnknownImage(View view, boolean large, int resId) {
|
|
||||||
if (resId == -1) resId = R.drawable.unknown_album;
|
|
||||||
if (large) {
|
|
||||||
setImageBitmap(view, null, largeUnknownImage, false);
|
|
||||||
} else {
|
|
||||||
if (view instanceof TextView) {
|
|
||||||
((TextView) view).setCompoundDrawablesWithIntrinsicBounds(resId, 0, 0, 0);
|
|
||||||
} else if (view instanceof ImageView) {
|
|
||||||
((ImageView) view).setImageResource(resId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addImageToCache(Bitmap bitmap, MusicDirectory.Entry entry, int size) {
|
|
||||||
cache.put(getKey(entry.getCoverArt(), size), bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addImageToCache(Bitmap bitmap, String username, int size) {
|
|
||||||
cache.put(getKey(username, size), bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
queue.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
while (running.get()) {
|
|
||||||
try {
|
|
||||||
Task task = queue.take();
|
|
||||||
task.execute();
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
running.set(false);
|
|
||||||
break;
|
|
||||||
} catch (Throwable x) {
|
|
||||||
Timber.e(x, "Unexpected exception in ImageLoader.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Task {
|
|
||||||
private final View view;
|
|
||||||
private final MusicDirectory.Entry entry;
|
|
||||||
private final String username;
|
|
||||||
private final Handler handler;
|
|
||||||
private final int size;
|
|
||||||
private final boolean saveToFile;
|
|
||||||
private final boolean crossFade;
|
|
||||||
private final boolean highQuality;
|
|
||||||
|
|
||||||
Task(View view, MusicDirectory.Entry entry, int size, boolean saveToFile, boolean crossFade, boolean highQuality) {
|
|
||||||
this.view = view;
|
|
||||||
this.entry = entry;
|
|
||||||
this.username = null;
|
|
||||||
this.size = size;
|
|
||||||
this.saveToFile = saveToFile;
|
|
||||||
this.crossFade = crossFade;
|
|
||||||
this.highQuality = highQuality;
|
|
||||||
handler = new Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
Task(View view, String username, int size, boolean saveToFile, boolean crossFade, boolean highQuality) {
|
|
||||||
this.view = view;
|
|
||||||
this.entry = null;
|
|
||||||
this.username = username;
|
|
||||||
this.size = size;
|
|
||||||
this.saveToFile = saveToFile;
|
|
||||||
this.crossFade = crossFade;
|
|
||||||
this.highQuality = highQuality;
|
|
||||||
handler = new Handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void execute() {
|
|
||||||
try {
|
|
||||||
MusicService musicService = MusicServiceFactory.getMusicService();
|
|
||||||
final boolean isAvatar = this.username != null && this.entry == null;
|
|
||||||
final Bitmap bitmap = this.entry != null ?
|
|
||||||
musicService.getCoverArt(entry, size, saveToFile, highQuality) :
|
|
||||||
musicService.getAvatar(username, size, saveToFile, highQuality);
|
|
||||||
|
|
||||||
if (bitmap == null) {
|
|
||||||
Timber.d("Found empty album art.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAvatar)
|
|
||||||
addImageToCache(bitmap, username, size);
|
|
||||||
else
|
|
||||||
addImageToCache(bitmap, entry, size);
|
|
||||||
|
|
||||||
handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isAvatar) {
|
|
||||||
setAvatarImageBitmap(view, username, bitmap, crossFade);
|
|
||||||
} else {
|
|
||||||
setImageBitmap(view, entry, bitmap, crossFade);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Throwable x) {
|
|
||||||
Timber.e(x, "Failed to download album art.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -217,7 +217,6 @@ class NavigationActivity : AppCompatActivity() {
|
||||||
if (item.itemId == R.id.menu_exit) {
|
if (item.itemId == R.id.menu_exit) {
|
||||||
setResult(Constants.RESULT_CLOSE_ALL)
|
setResult(Constants.RESULT_CLOSE_ALL)
|
||||||
mediaPlayerController.stopJukeboxService()
|
mediaPlayerController.stopJukeboxService()
|
||||||
imageLoaderProvider.getImageLoader().stopImageLoader()
|
|
||||||
finish()
|
finish()
|
||||||
exit()
|
exit()
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ class AlbumRowAdapter(
|
||||||
) : GenericRowAdapter<MusicDirectory.Entry>(
|
) : GenericRowAdapter<MusicDirectory.Entry>(
|
||||||
onItemClick,
|
onItemClick,
|
||||||
onContextMenuClick,
|
onContextMenuClick,
|
||||||
imageLoader,
|
|
||||||
onMusicFolderUpdate
|
onMusicFolderUpdate
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@ class ArtistRowAdapter(
|
||||||
) : GenericRowAdapter<Artist>(
|
) : GenericRowAdapter<Artist>(
|
||||||
onItemClick,
|
onItemClick,
|
||||||
onContextMenuClick,
|
onContextMenuClick,
|
||||||
imageLoader,
|
|
||||||
onMusicFolderUpdate
|
onMusicFolderUpdate
|
||||||
),
|
),
|
||||||
SectionedAdapter {
|
SectionedAdapter {
|
||||||
|
|
|
@ -20,7 +20,6 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||||
import org.moire.ultrasonic.domain.MusicFolder
|
import org.moire.ultrasonic.domain.MusicFolder
|
||||||
import org.moire.ultrasonic.util.ImageLoader
|
|
||||||
import org.moire.ultrasonic.view.SelectMusicFolderView
|
import org.moire.ultrasonic.view.SelectMusicFolderView
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -29,7 +28,6 @@ import org.moire.ultrasonic.view.SelectMusicFolderView
|
||||||
abstract class GenericRowAdapter<T>(
|
abstract class GenericRowAdapter<T>(
|
||||||
val onItemClick: (T) -> Unit,
|
val onItemClick: (T) -> Unit,
|
||||||
val onContextMenuClick: (MenuItem, T) -> Boolean,
|
val onContextMenuClick: (MenuItem, T) -> Boolean,
|
||||||
private val imageLoader: ImageLoader,
|
|
||||||
private val onMusicFolderUpdate: (String?) -> Unit
|
private val onMusicFolderUpdate: (String?) -> Unit
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
open var itemList: List<T> = listOf()
|
open var itemList: List<T> = listOf()
|
||||||
|
@ -94,13 +92,6 @@ abstract class GenericRowAdapter<T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
|
|
||||||
if ((holder is ViewHolder) && (holder.coverArtId != null)) {
|
|
||||||
imageLoader.cancel(holder.coverArtId)
|
|
||||||
}
|
|
||||||
super.onViewRecycled(holder)
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int)
|
abstract override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int)
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
|
|
|
@ -478,7 +478,7 @@ class MediaPlayerService : Service() {
|
||||||
if (currentPlaying != null) {
|
if (currentPlaying != null) {
|
||||||
try {
|
try {
|
||||||
val song = currentPlaying.song
|
val song = currentPlaying.song
|
||||||
val cover = FileUtil.getAlbumArtBitmap(
|
val cover = FileUtil.getAlbumArtBitmapFromDisk(
|
||||||
song, Util.getMinDisplayMetric(),
|
song, Util.getMinDisplayMetric(),
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
@ -648,7 +648,7 @@ class MediaPlayerService : Service() {
|
||||||
// Set song title, artist and cover if possible
|
// Set song title, artist and cover if possible
|
||||||
if (song != null) {
|
if (song != null) {
|
||||||
val iconSize = (256 * context.resources.displayMetrics.density).toInt()
|
val iconSize = (256 * context.resources.displayMetrics.density).toInt()
|
||||||
val bitmap = FileUtil.getAlbumArtBitmap(song, iconSize, true)
|
val bitmap = FileUtil.getAlbumArtBitmapFromDisk(song, iconSize, true)
|
||||||
notificationBuilder!!.setContentTitle(song.title)
|
notificationBuilder!!.setContentTitle(song.title)
|
||||||
notificationBuilder!!.setContentText(song.artist)
|
notificationBuilder!!.setContentText(song.artist)
|
||||||
notificationBuilder!!.setLargeIcon(bitmap)
|
notificationBuilder!!.setLargeIcon(bitmap)
|
||||||
|
|
|
@ -125,7 +125,7 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||||
highQuality: Boolean
|
highQuality: Boolean
|
||||||
): Bitmap? {
|
): Bitmap? {
|
||||||
return try {
|
return try {
|
||||||
val bitmap = FileUtil.getAvatarBitmap(username, size, highQuality)
|
val bitmap = FileUtil.getAvatarBitmapFromDisk(username, size, highQuality)
|
||||||
Util.scaleBitmap(bitmap, size)
|
Util.scaleBitmap(bitmap, size)
|
||||||
} catch (ignored: Exception) {
|
} catch (ignored: Exception) {
|
||||||
null
|
null
|
||||||
|
@ -139,7 +139,7 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||||
highQuality: Boolean
|
highQuality: Boolean
|
||||||
): Bitmap? {
|
): Bitmap? {
|
||||||
return try {
|
return try {
|
||||||
val bitmap = FileUtil.getAlbumArtBitmap(entry, size, highQuality)
|
val bitmap = FileUtil.getAlbumArtBitmapFromDisk(entry, size, highQuality)
|
||||||
Util.scaleBitmap(bitmap, size)
|
Util.scaleBitmap(bitmap, size)
|
||||||
} catch (ignored: Exception) {
|
} catch (ignored: Exception) {
|
||||||
null
|
null
|
||||||
|
|
|
@ -488,6 +488,9 @@ open class RESTMusicService(
|
||||||
return response.body()!!.starred2.toDomainEntity()
|
return response.body()!!.starred2.toDomainEntity()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Implement file caching in Picasso CoverArtRequestHandler,
|
||||||
|
// and then use Picasso to handle this cache
|
||||||
|
// This is only called by DownloadFile to cache the cover art for offline use
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
override fun getCoverArt(
|
override fun getCoverArt(
|
||||||
entry: MusicDirectory.Entry,
|
entry: MusicDirectory.Entry,
|
||||||
|
@ -499,7 +502,7 @@ open class RESTMusicService(
|
||||||
// the same song.
|
// the same song.
|
||||||
synchronized(entry) {
|
synchronized(entry) {
|
||||||
// Use cached file, if existing.
|
// Use cached file, if existing.
|
||||||
var bitmap = FileUtil.getAlbumArtBitmap(entry, size, highQuality)
|
var bitmap = FileUtil.getAlbumArtBitmapFromDisk(entry, size, highQuality)
|
||||||
val serverScaling = isServerScalingEnabled()
|
val serverScaling = isServerScalingEnabled()
|
||||||
|
|
||||||
if (bitmap == null) {
|
if (bitmap == null) {
|
||||||
|
@ -507,8 +510,9 @@ open class RESTMusicService(
|
||||||
|
|
||||||
val id = entry.coverArt
|
val id = entry.coverArt
|
||||||
|
|
||||||
|
// Can't load empty string ids
|
||||||
if (TextUtils.isEmpty(id)) {
|
if (TextUtils.isEmpty(id)) {
|
||||||
return null // Can't load
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val response = subsonicAPIClient.getCoverArt(id!!, size.toLong())
|
val response = subsonicAPIClient.getCoverArt(id!!, size.toLong())
|
||||||
|
@ -809,6 +813,9 @@ open class RESTMusicService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Implement file caching in Picasso AvatarRequestHandler,
|
||||||
|
// and then use Picasso to handle this cache
|
||||||
|
// This method is called from nowhere (all avatars are loaded directly using Picasso)
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
override fun getAvatar(
|
override fun getAvatar(
|
||||||
username: String?,
|
username: String?,
|
||||||
|
@ -824,7 +831,7 @@ open class RESTMusicService(
|
||||||
|
|
||||||
synchronized(username) {
|
synchronized(username) {
|
||||||
// Use cached file, if existing.
|
// Use cached file, if existing.
|
||||||
var bitmap = FileUtil.getAvatarBitmap(username, size, highQuality)
|
var bitmap = FileUtil.getAvatarBitmapFromDisk(username, size, highQuality)
|
||||||
|
|
||||||
if (bitmap == null) {
|
if (bitmap == null) {
|
||||||
var inputStream: InputStream? = null
|
var inputStream: InputStream? = null
|
||||||
|
|
|
@ -3,11 +3,7 @@ package org.moire.ultrasonic.subsonic
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.get
|
import org.koin.core.component.get
|
||||||
import org.moire.ultrasonic.featureflags.Feature
|
|
||||||
import org.moire.ultrasonic.featureflags.FeatureStorage
|
|
||||||
import org.moire.ultrasonic.util.ImageLoader
|
import org.moire.ultrasonic.util.ImageLoader
|
||||||
import org.moire.ultrasonic.util.LegacyImageLoader
|
|
||||||
import org.moire.ultrasonic.util.Util
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the lifetime of the Image Loader
|
* Handles the lifetime of the Image Loader
|
||||||
|
@ -17,33 +13,13 @@ class ImageLoaderProvider(val context: Context) : KoinComponent {
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun clearImageLoader() {
|
fun clearImageLoader() {
|
||||||
if (
|
|
||||||
imageLoader != null &&
|
|
||||||
imageLoader!!.isRunning
|
|
||||||
) {
|
|
||||||
imageLoader!!.clear()
|
|
||||||
}
|
|
||||||
imageLoader = null
|
imageLoader = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun getImageLoader(): ImageLoader {
|
fun getImageLoader(): ImageLoader {
|
||||||
if (imageLoader == null || !imageLoader!!.isRunning) {
|
if (imageLoader == null) {
|
||||||
val legacyImageLoader = LegacyImageLoader(
|
imageLoader = SubsonicImageLoaderProxy(get())
|
||||||
context,
|
|
||||||
Util.getImageLoaderConcurrency()
|
|
||||||
)
|
|
||||||
val features: FeatureStorage = get()
|
|
||||||
val isNewImageLoaderEnabled = features.isFeatureEnabled(Feature.NEW_IMAGE_DOWNLOADER)
|
|
||||||
imageLoader = if (isNewImageLoaderEnabled) {
|
|
||||||
SubsonicImageLoaderProxy(
|
|
||||||
legacyImageLoader,
|
|
||||||
get()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
legacyImageLoader
|
|
||||||
}
|
|
||||||
imageLoader!!.startImageLoader()
|
|
||||||
}
|
}
|
||||||
return imageLoader!!
|
return imageLoader!!
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,24 +9,21 @@ import org.moire.ultrasonic.domain.MusicDirectory
|
||||||
import org.moire.ultrasonic.subsonic.loader.image.ImageRequest
|
import org.moire.ultrasonic.subsonic.loader.image.ImageRequest
|
||||||
import org.moire.ultrasonic.subsonic.loader.image.SubsonicImageLoader
|
import org.moire.ultrasonic.subsonic.loader.image.SubsonicImageLoader
|
||||||
import org.moire.ultrasonic.util.ImageLoader
|
import org.moire.ultrasonic.util.ImageLoader
|
||||||
import org.moire.ultrasonic.util.LegacyImageLoader
|
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.Util
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Temporary proxy between new [SubsonicImageLoader] and [ImageLoader] interface and old
|
* Proxy between [SubsonicImageLoader] and the main App.
|
||||||
* [LegacyImageLoader] implementation.
|
* Needed to calculate values like the maximum image size,
|
||||||
*
|
* which we can't outside the main package.
|
||||||
* Should be removed on [LegacyImageLoader] removal.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class SubsonicImageLoaderProxy(
|
class SubsonicImageLoaderProxy(
|
||||||
legacyImageLoader: LegacyImageLoader,
|
|
||||||
private val subsonicImageLoader: SubsonicImageLoader
|
private val subsonicImageLoader: SubsonicImageLoader
|
||||||
) : ImageLoader by legacyImageLoader {
|
) : ImageLoader {
|
||||||
|
|
||||||
private var imageSizeLarge = Util.getMaxDisplayMetric()
|
private var imageSizeLarge = Util.getMaxDisplayMetric()
|
||||||
private var imageSizeDefault = 0
|
private var imageSizeDefault = 0
|
||||||
|
|
||||||
|
|
||||||
override fun loadImage(
|
override fun loadImage(
|
||||||
view: View?,
|
view: View?,
|
||||||
entry: MusicDirectory.Entry?,
|
entry: MusicDirectory.Entry?,
|
||||||
|
@ -87,7 +84,8 @@ class SubsonicImageLoaderProxy(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val default = ResourcesCompat.getDrawable(
|
val default = ResourcesCompat.getDrawable(
|
||||||
UApp.applicationContext().resources, R.drawable.unknown_album, null)
|
UApp.applicationContext().resources, R.drawable.unknown_album, null
|
||||||
|
)
|
||||||
|
|
||||||
// Determine the density-dependent image sizes by taking the fallback album
|
// Determine the density-dependent image sizes by taking the fallback album
|
||||||
// image and querying its size.
|
// image and querying its size.
|
||||||
|
|
|
@ -379,19 +379,6 @@
|
||||||
<string name="settings.show_all_songs_by_artist">Zobrazit všechny skladby umělce</string>
|
<string name="settings.show_all_songs_by_artist">Zobrazit všechny skladby umělce</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Přidat nový zápis v náhledu umělců pro přístup ke všem skladbám umělce</string>
|
<string name="settings.show_all_songs_by_artist_summary">Přidat nový zápis v náhledu umělců pro přístup ke všem skladbám umělce</string>
|
||||||
<string name="download.menu_show_artist">Zobrazit umělce</string>
|
<string name="download.menu_show_artist">Zobrazit umělce</string>
|
||||||
<string name="settings.image_loader_concurrency">Počet vláken stahování obrázků</string>
|
|
||||||
<string name="settings.image_loader_concurrency_1">1</string>
|
|
||||||
<string name="settings.image_loader_concurrency_2">2</string>
|
|
||||||
<string name="settings.image_loader_concurrency_3">3</string>
|
|
||||||
<string name="settings.image_loader_concurrency_4">4</string>
|
|
||||||
<string name="settings.image_loader_concurrency_5">5</string>
|
|
||||||
<string name="settings.image_loader_concurrency_6">6</string>
|
|
||||||
<string name="settings.image_loader_concurrency_7">7</string>
|
|
||||||
<string name="settings.image_loader_concurrency_8">8</string>
|
|
||||||
<string name="settings.image_loader_concurrency_9">9</string>
|
|
||||||
<string name="settings.image_loader_concurrency_10">10</string>
|
|
||||||
<string name="settings.image_loader_concurrency_11">11</string>
|
|
||||||
<string name="settings.image_loader_concurrency_12">12</string>
|
|
||||||
<string name="albumArt">albumArt</string>
|
<string name="albumArt">albumArt</string>
|
||||||
<string name="common_multiple_years">Vícenásobné roky</string>
|
<string name="common_multiple_years">Vícenásobné roky</string>
|
||||||
<string name="settings.playback.resume_on_bluetooth_device">Pokračovat v přehrávání po připojení bluetooth přístroje</string>
|
<string name="settings.playback.resume_on_bluetooth_device">Pokračovat v přehrávání po připojení bluetooth přístroje</string>
|
||||||
|
@ -501,10 +488,6 @@
|
||||||
|
|
||||||
<!-- Subsonic feature flags -->
|
<!-- Subsonic feature flags -->
|
||||||
<string name="feature_flags_category_title">Příznaky funkcí</string>
|
<string name="feature_flags_category_title">Příznaky funkcí</string>
|
||||||
<string name="feature_flags_image_loader_title">Povolit nové načítání obrázků</string>
|
|
||||||
<string name="feature_flags_image_loader_description">Zapne novou implementaci načítání obrázků.
|
|
||||||
Toto aktuálně neukládá obrázky dlouhodobě, ale používá pouze odkládání do paměti.
|
|
||||||
</string>
|
|
||||||
<string name="feature_flags_five_star_rating_title">Používat pět hvězdiček pro hodnocení skladeb</string>
|
<string name="feature_flags_five_star_rating_title">Používat pět hvězdiček pro hodnocení skladeb</string>
|
||||||
<string name="feature_flags_five_star_rating_description">Používat pět hvězdiček pro hodnocení skladeb
|
<string name="feature_flags_five_star_rating_description">Používat pět hvězdiček pro hodnocení skladeb
|
||||||
namísto jednoduchého jednohvězdičkového hodnocení.
|
namísto jednoduchého jednohvězdičkového hodnocení.
|
||||||
|
|
|
@ -376,19 +376,6 @@
|
||||||
<string name="settings.show_all_songs_by_artist">Alle Titel nach Künstler sortieren</string>
|
<string name="settings.show_all_songs_by_artist">Alle Titel nach Künstler sortieren</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Einen neuen Eintrag in der Künstleransicht hinzufügen, um auf alle Lieder eines Künstlers zuzugreifen</string>
|
<string name="settings.show_all_songs_by_artist_summary">Einen neuen Eintrag in der Künstleransicht hinzufügen, um auf alle Lieder eines Künstlers zuzugreifen</string>
|
||||||
<string name="download.menu_show_artist">Künstler zeigen</string>
|
<string name="download.menu_show_artist">Künstler zeigen</string>
|
||||||
<string name="settings.image_loader_concurrency">Paralleles laden von Bildern</string>
|
|
||||||
<string name="settings.image_loader_concurrency_1">1</string>
|
|
||||||
<string name="settings.image_loader_concurrency_2">2</string>
|
|
||||||
<string name="settings.image_loader_concurrency_3">3</string>
|
|
||||||
<string name="settings.image_loader_concurrency_4">4</string>
|
|
||||||
<string name="settings.image_loader_concurrency_5">5</string>
|
|
||||||
<string name="settings.image_loader_concurrency_6">6</string>
|
|
||||||
<string name="settings.image_loader_concurrency_7">7</string>
|
|
||||||
<string name="settings.image_loader_concurrency_8">8</string>
|
|
||||||
<string name="settings.image_loader_concurrency_9">9</string>
|
|
||||||
<string name="settings.image_loader_concurrency_10">10</string>
|
|
||||||
<string name="settings.image_loader_concurrency_11">11</string>
|
|
||||||
<string name="settings.image_loader_concurrency_12">12</string>
|
|
||||||
<string name="common_multiple_years">Mehrere Jahre</string>
|
<string name="common_multiple_years">Mehrere Jahre</string>
|
||||||
<string name="server_editor.new_label">Server hinzufügen</string>
|
<string name="server_editor.new_label">Server hinzufügen</string>
|
||||||
<plurals name="select_album_n_songs">
|
<plurals name="select_album_n_songs">
|
||||||
|
@ -434,8 +421,6 @@
|
||||||
|
|
||||||
<!-- Subsonic feature flags -->
|
<!-- Subsonic feature flags -->
|
||||||
<string name="feature_flags_category_title">Funktionseinstellungem</string>
|
<string name="feature_flags_category_title">Funktionseinstellungem</string>
|
||||||
<string name="feature_flags_image_loader_title">Neuen Bild-Lader aktivieren</string>
|
|
||||||
<string name="feature_flags_image_loader_description">Neuen Bild-Lader aktivieren. Bilder werden derzeit nur im Chache gespeichert.</string>
|
|
||||||
<string name="feature_flags_five_star_rating_title">Verwenden Sie Fünf-Sterne-Bewertung für Songs</string>
|
<string name="feature_flags_five_star_rating_title">Verwenden Sie Fünf-Sterne-Bewertung für Songs</string>
|
||||||
<string name="feature_flags_five_star_rating_description">Verwenden Sie Fünf-Sterne-Bewertungssystem für Songs
|
<string name="feature_flags_five_star_rating_description">Verwenden Sie Fünf-Sterne-Bewertungssystem für Songs
|
||||||
anstatt einfach Elemente zu markieren / zu entfernen.
|
anstatt einfach Elemente zu markieren / zu entfernen.
|
||||||
|
|
|
@ -393,19 +393,6 @@
|
||||||
<string name="settings.show_all_songs_by_artist">Mostrar todas las canciones por artista</string>
|
<string name="settings.show_all_songs_by_artist">Mostrar todas las canciones por artista</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Añadir nueva entrada en la vista de artista para acceder a todas las canciones de un artista</string>
|
<string name="settings.show_all_songs_by_artist_summary">Añadir nueva entrada en la vista de artista para acceder a todas las canciones de un artista</string>
|
||||||
<string name="download.menu_show_artist">Mostrar artista</string>
|
<string name="download.menu_show_artist">Mostrar artista</string>
|
||||||
<string name="settings.image_loader_concurrency">Concurrencia del cargador de imágenes</string>
|
|
||||||
<string name="settings.image_loader_concurrency_1">1</string>
|
|
||||||
<string name="settings.image_loader_concurrency_2">2</string>
|
|
||||||
<string name="settings.image_loader_concurrency_3">3</string>
|
|
||||||
<string name="settings.image_loader_concurrency_4">4</string>
|
|
||||||
<string name="settings.image_loader_concurrency_5">5</string>
|
|
||||||
<string name="settings.image_loader_concurrency_6">6</string>
|
|
||||||
<string name="settings.image_loader_concurrency_7">7</string>
|
|
||||||
<string name="settings.image_loader_concurrency_8">8</string>
|
|
||||||
<string name="settings.image_loader_concurrency_9">9</string>
|
|
||||||
<string name="settings.image_loader_concurrency_10">10</string>
|
|
||||||
<string name="settings.image_loader_concurrency_11">11</string>
|
|
||||||
<string name="settings.image_loader_concurrency_12">12</string>
|
|
||||||
<string name="albumArt">Caratula del Álbum</string>
|
<string name="albumArt">Caratula del Álbum</string>
|
||||||
<string name="common_multiple_years">Múltiples años</string>
|
<string name="common_multiple_years">Múltiples años</string>
|
||||||
<string name="settings.playback.resume_on_bluetooth_device">Reanudar al conectar un dispositivo Bluetooth</string>
|
<string name="settings.playback.resume_on_bluetooth_device">Reanudar al conectar un dispositivo Bluetooth</string>
|
||||||
|
@ -503,10 +490,6 @@
|
||||||
|
|
||||||
<!-- Subsonic feature flags -->
|
<!-- Subsonic feature flags -->
|
||||||
<string name="feature_flags_category_title">Funciones experimentales</string>
|
<string name="feature_flags_category_title">Funciones experimentales</string>
|
||||||
<string name="feature_flags_image_loader_title">Habilitar nuevo cargador de imágenes</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_five_star_rating_title">Use cinco estrellas para las canciones</string>
|
<string name="feature_flags_five_star_rating_title">Use cinco estrellas para las canciones</string>
|
||||||
<string name="feature_flags_five_star_rating_description">Utilice el sistema de calificación de cinco estrellas para canciones
|
<string name="feature_flags_five_star_rating_description">Utilice el sistema de calificación de cinco estrellas para canciones
|
||||||
en lugar de simplemente destacar / desestimar elementos.
|
en lugar de simplemente destacar / desestimar elementos.
|
||||||
|
|
|
@ -381,19 +381,6 @@
|
||||||
<string name="settings.show_all_songs_by_artist">Voir tous les titres par artiste</string>
|
<string name="settings.show_all_songs_by_artist">Voir tous les titres par artiste</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Ajouter une nouvelle entrée dans la vue artiste pour accéder à toutes les titres d\'un artiste</string>
|
<string name="settings.show_all_songs_by_artist_summary">Ajouter une nouvelle entrée dans la vue artiste pour accéder à toutes les titres d\'un artiste</string>
|
||||||
<string name="download.menu_show_artist">Afficher l\'artiste</string>
|
<string name="download.menu_show_artist">Afficher l\'artiste</string>
|
||||||
<string name="settings.image_loader_concurrency">Chargements d’images simultanés</string>
|
|
||||||
<string name="settings.image_loader_concurrency_1">1</string>
|
|
||||||
<string name="settings.image_loader_concurrency_2">2</string>
|
|
||||||
<string name="settings.image_loader_concurrency_3">3</string>
|
|
||||||
<string name="settings.image_loader_concurrency_4">4</string>
|
|
||||||
<string name="settings.image_loader_concurrency_5">5</string>
|
|
||||||
<string name="settings.image_loader_concurrency_6">6</string>
|
|
||||||
<string name="settings.image_loader_concurrency_7">7</string>
|
|
||||||
<string name="settings.image_loader_concurrency_8">8</string>
|
|
||||||
<string name="settings.image_loader_concurrency_9">9</string>
|
|
||||||
<string name="settings.image_loader_concurrency_10">10</string>
|
|
||||||
<string name="settings.image_loader_concurrency_11">11</string>
|
|
||||||
<string name="settings.image_loader_concurrency_12">12</string>
|
|
||||||
<string name="albumArt">Pochette d\'album</string>
|
<string name="albumArt">Pochette d\'album</string>
|
||||||
<string name="common_multiple_years">Années multiples</string>
|
<string name="common_multiple_years">Années multiples</string>
|
||||||
<string name="settings.playback.resume_on_bluetooth_device">Reprendre lorsqu’un appareil Bluetooth se connecte</string>
|
<string name="settings.playback.resume_on_bluetooth_device">Reprendre lorsqu’un appareil Bluetooth se connecte</string>
|
||||||
|
@ -491,10 +478,6 @@
|
||||||
|
|
||||||
<!-- Subsonic feature flags -->
|
<!-- Subsonic feature flags -->
|
||||||
<string name="feature_flags_category_title">Drapeaux des fonctionnalités</string>
|
<string name="feature_flags_category_title">Drapeaux des fonctionnalités</string>
|
||||||
<string name="feature_flags_image_loader_title">Activer le nouveau chargeur d\'images</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 l\'appareil et n\'utilise que le cache en mémoire.
|
|
||||||
</string>
|
|
||||||
<string name="feature_flags_five_star_rating_title">Utiliser les étoiles pour noter les morceaux</string>
|
<string name="feature_flags_five_star_rating_title">Utiliser les étoiles pour noter les morceaux</string>
|
||||||
<string name="feature_flags_five_star_rating_description">Utiliser un système de notation à base d\'étoiles pour les morceaux
|
<string name="feature_flags_five_star_rating_description">Utiliser un système de notation à base d\'étoiles pour les morceaux
|
||||||
au lieu de simplement mettre en avant les morceaux.
|
au lieu de simplement mettre en avant les morceaux.
|
||||||
|
|
|
@ -393,19 +393,6 @@
|
||||||
<string name="settings.show_all_songs_by_artist">Az előadó összes dalának megjelenítése</string>
|
<string name="settings.show_all_songs_by_artist">Az előadó összes dalának megjelenítése</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Új bejegyzés hozzáadása az előadóhoz, az előadó összes dalának eléréséhez.</string>
|
<string name="settings.show_all_songs_by_artist_summary">Új bejegyzés hozzáadása az előadóhoz, az előadó összes dalának eléréséhez.</string>
|
||||||
<string name="download.menu_show_artist">Ugrás az előadóhoz</string>
|
<string name="download.menu_show_artist">Ugrás az előadóhoz</string>
|
||||||
<string name="settings.image_loader_concurrency">Image Loader Concurrency</string>
|
|
||||||
<string name="settings.image_loader_concurrency_1">1</string>
|
|
||||||
<string name="settings.image_loader_concurrency_2">2</string>
|
|
||||||
<string name="settings.image_loader_concurrency_3">3</string>
|
|
||||||
<string name="settings.image_loader_concurrency_4">4</string>
|
|
||||||
<string name="settings.image_loader_concurrency_5">5</string>
|
|
||||||
<string name="settings.image_loader_concurrency_6">6</string>
|
|
||||||
<string name="settings.image_loader_concurrency_7">7</string>
|
|
||||||
<string name="settings.image_loader_concurrency_8">8</string>
|
|
||||||
<string name="settings.image_loader_concurrency_9">9</string>
|
|
||||||
<string name="settings.image_loader_concurrency_10">10</string>
|
|
||||||
<string name="settings.image_loader_concurrency_11">11</string>
|
|
||||||
<string name="settings.image_loader_concurrency_12">12</string>
|
|
||||||
<string name="albumArt">albumArt</string>
|
<string name="albumArt">albumArt</string>
|
||||||
<string name="common_multiple_years">Több év</string>
|
<string name="common_multiple_years">Több év</string>
|
||||||
<string name="settings.playback.resume_on_bluetooth_device">Folytatás Bluetooth eszköz csatlakozásakor</string>
|
<string name="settings.playback.resume_on_bluetooth_device">Folytatás Bluetooth eszköz csatlakozásakor</string>
|
||||||
|
@ -501,10 +488,6 @@
|
||||||
|
|
||||||
<!-- Subsonic feature flags -->
|
<!-- Subsonic feature flags -->
|
||||||
<string name="feature_flags_category_title">Jellemzők Zászlók</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>
|
|
||||||
<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_five_star_rating_title">Öt csillagos értékelés használata a dalokhoz</string>
|
<string name="feature_flags_five_star_rating_title">Öt csillagos értékelés használata a dalokhoz</string>
|
||||||
<string name="feature_flags_five_star_rating_description">Öt csillag használata az értékeléshez az egyszerű
|
<string name="feature_flags_five_star_rating_description">Öt csillag használata az értékeléshez az egyszerű
|
||||||
csillaggal jelölés helyett.
|
csillaggal jelölés helyett.
|
||||||
|
|
|
@ -338,18 +338,6 @@
|
||||||
<string name="share_via">Condividi canzoni via</string>
|
<string name="share_via">Condividi canzoni via</string>
|
||||||
<string name="settings.video_mx_player">MX Player</string>
|
<string name="settings.video_mx_player">MX Player</string>
|
||||||
<string name="settings.video_default">Predefinito</string>
|
<string name="settings.video_default">Predefinito</string>
|
||||||
<string name="settings.image_loader_concurrency_1">1</string>
|
|
||||||
<string name="settings.image_loader_concurrency_2">2</string>
|
|
||||||
<string name="settings.image_loader_concurrency_3">3</string>
|
|
||||||
<string name="settings.image_loader_concurrency_4">4</string>
|
|
||||||
<string name="settings.image_loader_concurrency_5">5</string>
|
|
||||||
<string name="settings.image_loader_concurrency_6">6</string>
|
|
||||||
<string name="settings.image_loader_concurrency_7">7</string>
|
|
||||||
<string name="settings.image_loader_concurrency_8">8</string>
|
|
||||||
<string name="settings.image_loader_concurrency_9">9</string>
|
|
||||||
<string name="settings.image_loader_concurrency_10">10</string>
|
|
||||||
<string name="settings.image_loader_concurrency_11">11</string>
|
|
||||||
<string name="settings.image_loader_concurrency_12">12</string>
|
|
||||||
<plurals name="select_album_n_songs">
|
<plurals name="select_album_n_songs">
|
||||||
<item quantity="one">1 canzone</item>
|
<item quantity="one">1 canzone</item>
|
||||||
<item quantity="other">%d canzoni</item>
|
<item quantity="other">%d canzoni</item>
|
||||||
|
|
|
@ -393,19 +393,6 @@
|
||||||
<string name="settings.show_all_songs_by_artist">Alle nummers van artiest tonen</string>
|
<string name="settings.show_all_songs_by_artist">Alle nummers van artiest tonen</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Item toevoegen in artiestweergave om alle nummers van een artiest te bekijken</string>
|
<string name="settings.show_all_songs_by_artist_summary">Item toevoegen in artiestweergave om alle nummers van een artiest te bekijken</string>
|
||||||
<string name="download.menu_show_artist">Artiest tonen</string>
|
<string name="download.menu_show_artist">Artiest tonen</string>
|
||||||
<string name="settings.image_loader_concurrency">Aantal tegelijkertijd te laden afbeeldingen</string>
|
|
||||||
<string name="settings.image_loader_concurrency_1">1</string>
|
|
||||||
<string name="settings.image_loader_concurrency_2">2</string>
|
|
||||||
<string name="settings.image_loader_concurrency_3">3</string>
|
|
||||||
<string name="settings.image_loader_concurrency_4">4</string>
|
|
||||||
<string name="settings.image_loader_concurrency_5">5</string>
|
|
||||||
<string name="settings.image_loader_concurrency_6">6</string>
|
|
||||||
<string name="settings.image_loader_concurrency_7">7</string>
|
|
||||||
<string name="settings.image_loader_concurrency_8">8</string>
|
|
||||||
<string name="settings.image_loader_concurrency_9">9</string>
|
|
||||||
<string name="settings.image_loader_concurrency_10">10</string>
|
|
||||||
<string name="settings.image_loader_concurrency_11">11</string>
|
|
||||||
<string name="settings.image_loader_concurrency_12">12</string>
|
|
||||||
<string name="albumArt">Albumhoes</string>
|
<string name="albumArt">Albumhoes</string>
|
||||||
<string name="common_multiple_years">Meerdere jaren</string>
|
<string name="common_multiple_years">Meerdere jaren</string>
|
||||||
<string name="settings.playback.resume_on_bluetooth_device">Hervatten bij verbinding met bluetoothapparaat</string>
|
<string name="settings.playback.resume_on_bluetooth_device">Hervatten bij verbinding met bluetoothapparaat</string>
|
||||||
|
@ -503,10 +490,6 @@
|
||||||
|
|
||||||
<!-- Subsonic feature flags -->
|
<!-- Subsonic feature flags -->
|
||||||
<string name="feature_flags_category_title">Experimentele functies</string>
|
<string name="feature_flags_category_title">Experimentele functies</string>
|
||||||
<string name="feature_flags_image_loader_title">Nieuwe manier van afbeeldingen laden inschakelen</string>
|
|
||||||
<string name="feature_flags_image_loader_description">Schakelt de nieuwe methode voor het laden van afbeeldingen in.
|
|
||||||
Momenteel slaat het geen afbeeldingen op op de apparaatopslag en wordt alleen geheugencache gebruikt.
|
|
||||||
</string>
|
|
||||||
<string name="feature_flags_five_star_rating_title">Gebruik vijf sterren voor nummers</string>
|
<string name="feature_flags_five_star_rating_title">Gebruik vijf sterren voor nummers</string>
|
||||||
<string name="feature_flags_five_star_rating_description">Gebruik vijf sterren ratingsysteem voor liedjes
|
<string name="feature_flags_five_star_rating_description">Gebruik vijf sterren ratingsysteem voor liedjes
|
||||||
in plaats van items simpelweg in de hoofdrol te zetten / niet te verwijderen.
|
in plaats van items simpelweg in de hoofdrol te zetten / niet te verwijderen.
|
||||||
|
|
|
@ -376,19 +376,6 @@ ponieważ api Subsonic nie wspiera nowego sposobu autoryzacji dla użytkowników
|
||||||
<string name="settings.show_all_songs_by_artist">Wyświetlaj wszystkie utwory artysty</string>
|
<string name="settings.show_all_songs_by_artist">Wyświetlaj wszystkie utwory artysty</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Dodaje nową pozycję w widoku artysty z wszystkimi jego utworami</string>
|
<string name="settings.show_all_songs_by_artist_summary">Dodaje nową pozycję w widoku artysty z wszystkimi jego utworami</string>
|
||||||
<string name="download.menu_show_artist">Wyświetlaj artystę</string>
|
<string name="download.menu_show_artist">Wyświetlaj artystę</string>
|
||||||
<string name="settings.image_loader_concurrency">Ilość jednocześnie ładowanych obrazów</string>
|
|
||||||
<string name="settings.image_loader_concurrency_1">1</string>
|
|
||||||
<string name="settings.image_loader_concurrency_2">2</string>
|
|
||||||
<string name="settings.image_loader_concurrency_3">3</string>
|
|
||||||
<string name="settings.image_loader_concurrency_4">4</string>
|
|
||||||
<string name="settings.image_loader_concurrency_5">5</string>
|
|
||||||
<string name="settings.image_loader_concurrency_6">6</string>
|
|
||||||
<string name="settings.image_loader_concurrency_7">7</string>
|
|
||||||
<string name="settings.image_loader_concurrency_8">8</string>
|
|
||||||
<string name="settings.image_loader_concurrency_9">9</string>
|
|
||||||
<string name="settings.image_loader_concurrency_10">10</string>
|
|
||||||
<string name="settings.image_loader_concurrency_11">11</string>
|
|
||||||
<string name="settings.image_loader_concurrency_12">12</string>
|
|
||||||
<string name="albumArt">Okładka</string>
|
<string name="albumArt">Okładka</string>
|
||||||
<string name="common_multiple_years">Z różnych lat</string>
|
<string name="common_multiple_years">Z różnych lat</string>
|
||||||
<string name="server_editor.new_label">Dodaj serwer</string>
|
<string name="server_editor.new_label">Dodaj serwer</string>
|
||||||
|
@ -449,9 +436,6 @@ ponieważ api Subsonic nie wspiera nowego sposobu autoryzacji dla użytkowników
|
||||||
|
|
||||||
<!-- Subsonic feature flags -->
|
<!-- Subsonic feature flags -->
|
||||||
<string name="feature_flags_category_title">Flagi funkcji</string>
|
<string name="feature_flags_category_title">Flagi funkcji</string>
|
||||||
<string name="feature_flags_image_loader_title">Włącz program ładujący nowe obrazy</string>
|
|
||||||
<string name="feature_flags_image_loader_description">Włącza implementację modułu ładującego nowe obrazy.
|
|
||||||
Obecnie nie zapisuje obrazów w pamięci urządzenia, tylko wykorzystuje tylko pamięć podręczną.</string>
|
|
||||||
<string name="feature_flags_five_star_rating_title">Użyj pięciu gwiazdek dla utworów</string>
|
<string name="feature_flags_five_star_rating_title">Użyj pięciu gwiazdek dla utworów</string>
|
||||||
<string name="feature_flags_five_star_rating_description">W przypadku utworów użyj systemu pięciu gwiazdek
|
<string name="feature_flags_five_star_rating_description">W przypadku utworów użyj systemu pięciu gwiazdek
|
||||||
zamiast po prostu grać gwiazdkami / bez gwiazd.
|
zamiast po prostu grać gwiazdkami / bez gwiazd.
|
||||||
|
|
|
@ -381,19 +381,6 @@
|
||||||
<string name="settings.show_all_songs_by_artist">Mostrar Todas as Músicas por Artista</string>
|
<string name="settings.show_all_songs_by_artist">Mostrar Todas as Músicas por Artista</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Adicionar nova entrada em artista para acessar todas as músicas do artista</string>
|
<string name="settings.show_all_songs_by_artist_summary">Adicionar nova entrada em artista para acessar todas as músicas do artista</string>
|
||||||
<string name="download.menu_show_artist">Mostrar Artista</string>
|
<string name="download.menu_show_artist">Mostrar Artista</string>
|
||||||
<string name="settings.image_loader_concurrency">Concorrência ao Carregar Imagens</string>
|
|
||||||
<string name="settings.image_loader_concurrency_1">1</string>
|
|
||||||
<string name="settings.image_loader_concurrency_2">2</string>
|
|
||||||
<string name="settings.image_loader_concurrency_3">3</string>
|
|
||||||
<string name="settings.image_loader_concurrency_4">4</string>
|
|
||||||
<string name="settings.image_loader_concurrency_5">5</string>
|
|
||||||
<string name="settings.image_loader_concurrency_6">6</string>
|
|
||||||
<string name="settings.image_loader_concurrency_7">7</string>
|
|
||||||
<string name="settings.image_loader_concurrency_8">8</string>
|
|
||||||
<string name="settings.image_loader_concurrency_9">9</string>
|
|
||||||
<string name="settings.image_loader_concurrency_10">10</string>
|
|
||||||
<string name="settings.image_loader_concurrency_11">11</string>
|
|
||||||
<string name="settings.image_loader_concurrency_12">12</string>
|
|
||||||
<string name="albumArt">albumArt</string>
|
<string name="albumArt">albumArt</string>
|
||||||
<string name="common_multiple_years">Anos Múltiplos</string>
|
<string name="common_multiple_years">Anos Múltiplos</string>
|
||||||
<string name="settings.playback.resume_on_bluetooth_device">Retomar ao Conectar Dispositivo Bluetooth</string>
|
<string name="settings.playback.resume_on_bluetooth_device">Retomar ao Conectar Dispositivo Bluetooth</string>
|
||||||
|
@ -489,10 +476,6 @@
|
||||||
|
|
||||||
<!-- Subsonic feature flags -->
|
<!-- Subsonic feature flags -->
|
||||||
<string name="feature_flags_category_title">Bandeiras de Recursos</string>
|
<string name="feature_flags_category_title">Bandeiras de Recursos</string>
|
||||||
<string name="feature_flags_image_loader_title">Ativar Novo Carregador de Imagens</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_five_star_rating_title">Usar Classif. de 5 estrelas Para Músicas</string>
|
<string name="feature_flags_five_star_rating_title">Usar Classif. de 5 estrelas Para Músicas</string>
|
||||||
<string name="feature_flags_five_star_rating_description">Use o sistema de classificação de 5 estrelas para músicas
|
<string name="feature_flags_five_star_rating_description">Use o sistema de classificação de 5 estrelas para músicas
|
||||||
em vez de simplesmente estrelar/não estrelar itens.
|
em vez de simplesmente estrelar/não estrelar itens.
|
||||||
|
|
|
@ -376,19 +376,6 @@
|
||||||
<string name="settings.show_all_songs_by_artist">Todas as Músicas do Artista</string>
|
<string name="settings.show_all_songs_by_artist">Todas as Músicas do Artista</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Adicionar nova entrada em artista para ver todas as músicas do artista</string>
|
<string name="settings.show_all_songs_by_artist_summary">Adicionar nova entrada em artista para ver todas as músicas do artista</string>
|
||||||
<string name="download.menu_show_artist">Mostrar Artista</string>
|
<string name="download.menu_show_artist">Mostrar Artista</string>
|
||||||
<string name="settings.image_loader_concurrency">Concorrência ao Carregar Imagens</string>
|
|
||||||
<string name="settings.image_loader_concurrency_1">1</string>
|
|
||||||
<string name="settings.image_loader_concurrency_2">2</string>
|
|
||||||
<string name="settings.image_loader_concurrency_3">3</string>
|
|
||||||
<string name="settings.image_loader_concurrency_4">4</string>
|
|
||||||
<string name="settings.image_loader_concurrency_5">5</string>
|
|
||||||
<string name="settings.image_loader_concurrency_6">6</string>
|
|
||||||
<string name="settings.image_loader_concurrency_7">7</string>
|
|
||||||
<string name="settings.image_loader_concurrency_8">8</string>
|
|
||||||
<string name="settings.image_loader_concurrency_9">9</string>
|
|
||||||
<string name="settings.image_loader_concurrency_10">10</string>
|
|
||||||
<string name="settings.image_loader_concurrency_11">11</string>
|
|
||||||
<string name="settings.image_loader_concurrency_12">12</string>
|
|
||||||
<string name="common_multiple_years">Múltiplos Anos</string>
|
<string name="common_multiple_years">Múltiplos Anos</string>
|
||||||
<string name="server_editor.new_label">Adicionar Servidor</string>
|
<string name="server_editor.new_label">Adicionar Servidor</string>
|
||||||
<plurals name="select_album_n_songs">
|
<plurals name="select_album_n_songs">
|
||||||
|
@ -434,10 +421,6 @@
|
||||||
|
|
||||||
<!-- Subsonic feature flags -->
|
<!-- Subsonic feature flags -->
|
||||||
<string name="feature_flags_category_title">Bandeiras de recursos</string>
|
<string name="feature_flags_category_title">Bandeiras de recursos</string>
|
||||||
<string name="feature_flags_image_loader_title">Ativar novo carregador de imagens</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_five_star_rating_title">Use classificação de cinco estrelas para músicas</string>
|
<string name="feature_flags_five_star_rating_title">Use classificação de cinco estrelas para músicas</string>
|
||||||
<string name="feature_flags_five_star_rating_description">Use o sistema de classificação de cinco estrelas para músicas
|
<string name="feature_flags_five_star_rating_description">Use o sistema de classificação de cinco estrelas para músicas
|
||||||
em vez de simplesmente estrelar / não estrelar itens.
|
em vez de simplesmente estrelar / não estrelar itens.
|
||||||
|
|
|
@ -368,19 +368,6 @@
|
||||||
<string name="settings.show_all_songs_by_artist">Показать все треки исполнителя</string>
|
<string name="settings.show_all_songs_by_artist">Показать все треки исполнителя</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Добавить новую запись в представлении исполнителя, чтобы получить доступ ко всем песням для исполнителя</string>
|
<string name="settings.show_all_songs_by_artist_summary">Добавить новую запись в представлении исполнителя, чтобы получить доступ ко всем песням для исполнителя</string>
|
||||||
<string name="download.menu_show_artist">Показать исполнителей</string>
|
<string name="download.menu_show_artist">Показать исполнителей</string>
|
||||||
<string name="settings.image_loader_concurrency">Загрузчик совпадающих изображений</string>
|
|
||||||
<string name="settings.image_loader_concurrency_1">1</string>
|
|
||||||
<string name="settings.image_loader_concurrency_2">2</string>
|
|
||||||
<string name="settings.image_loader_concurrency_3">3</string>
|
|
||||||
<string name="settings.image_loader_concurrency_4">4</string>
|
|
||||||
<string name="settings.image_loader_concurrency_5">5</string>
|
|
||||||
<string name="settings.image_loader_concurrency_6">6</string>
|
|
||||||
<string name="settings.image_loader_concurrency_7">7</string>
|
|
||||||
<string name="settings.image_loader_concurrency_8">8</string>
|
|
||||||
<string name="settings.image_loader_concurrency_9">9</string>
|
|
||||||
<string name="settings.image_loader_concurrency_10">10</string>
|
|
||||||
<string name="settings.image_loader_concurrency_11">11</string>
|
|
||||||
<string name="settings.image_loader_concurrency_12">12</string>
|
|
||||||
<string name="albumArt">albumArt</string>
|
<string name="albumArt">albumArt</string>
|
||||||
<string name="common_multiple_years">Несколько лет</string>
|
<string name="common_multiple_years">Несколько лет</string>
|
||||||
<!-- Subsonic api errors -->
|
<!-- Subsonic api errors -->
|
||||||
|
@ -396,7 +383,4 @@
|
||||||
|
|
||||||
<!-- Subsonic feature flags -->
|
<!-- Subsonic feature flags -->
|
||||||
<string name="feature_flags_category_title">Флаги</string>
|
<string name="feature_flags_category_title">Флаги</string>
|
||||||
<string name="feature_flags_image_loader_title">Включить новый загрузчик изображений</string>
|
|
||||||
<string name="feature_flags_image_loader_description">Включает новую реализацию загрузчика изображений.
|
|
||||||
В настоящее время он не сохраняет изображение в памяти устройства и использует только кэш в памяти.</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -264,18 +264,6 @@
|
||||||
<string name="settings.video_mx_player">MX Player</string>
|
<string name="settings.video_mx_player">MX Player</string>
|
||||||
<string name="settings.video_default">默认</string>
|
<string name="settings.video_default">默认</string>
|
||||||
<string name="menu.share">分享</string>
|
<string name="menu.share">分享</string>
|
||||||
<string name="settings.image_loader_concurrency_1">1</string>
|
|
||||||
<string name="settings.image_loader_concurrency_2">2</string>
|
|
||||||
<string name="settings.image_loader_concurrency_3">3</string>
|
|
||||||
<string name="settings.image_loader_concurrency_4">4</string>
|
|
||||||
<string name="settings.image_loader_concurrency_5">5</string>
|
|
||||||
<string name="settings.image_loader_concurrency_6">6</string>
|
|
||||||
<string name="settings.image_loader_concurrency_7">7</string>
|
|
||||||
<string name="settings.image_loader_concurrency_8">8</string>
|
|
||||||
<string name="settings.image_loader_concurrency_9">9</string>
|
|
||||||
<string name="settings.image_loader_concurrency_10">10</string>
|
|
||||||
<string name="settings.image_loader_concurrency_11">11</string>
|
|
||||||
<string name="settings.image_loader_concurrency_12">12</string>
|
|
||||||
<string name="settings.playback.bluetooth_disabled">已禁用</string>
|
<string name="settings.playback.bluetooth_disabled">已禁用</string>
|
||||||
<string name="settings.debug.log_delete">删除文件</string>
|
<string name="settings.debug.log_delete">删除文件</string>
|
||||||
<string name="settings.debug.log_deleted">删除日志文件</string>
|
<string name="settings.debug.log_deleted">删除日志文件</string>
|
||||||
|
@ -307,5 +295,4 @@
|
||||||
<string name="api.subsonic.upgrade_client">版本不兼容,请升级 Ultrasonic 应用。</string>
|
<string name="api.subsonic.upgrade_client">版本不兼容,请升级 Ultrasonic 应用。</string>
|
||||||
<string name="api.subsonic.upgrade_server">不兼容的版本。请升级Subsonic 服务。</string>
|
<string name="api.subsonic.upgrade_server">不兼容的版本。请升级Subsonic 服务。</string>
|
||||||
|
|
||||||
<string name="feature_flags_image_loader_title">启用新的图像加载器</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -395,19 +395,6 @@
|
||||||
<string name="settings.show_all_songs_by_artist">Show All Songs By Artist</string>
|
<string name="settings.show_all_songs_by_artist">Show All Songs By Artist</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Add new entry in artist view to access all songs for an artist</string>
|
<string name="settings.show_all_songs_by_artist_summary">Add new entry in artist view to access all songs for an artist</string>
|
||||||
<string name="download.menu_show_artist">Show Artist</string>
|
<string name="download.menu_show_artist">Show Artist</string>
|
||||||
<string name="settings.image_loader_concurrency">Image Loader Concurrency</string>
|
|
||||||
<string name="settings.image_loader_concurrency_1">1</string>
|
|
||||||
<string name="settings.image_loader_concurrency_2">2</string>
|
|
||||||
<string name="settings.image_loader_concurrency_3">3</string>
|
|
||||||
<string name="settings.image_loader_concurrency_4">4</string>
|
|
||||||
<string name="settings.image_loader_concurrency_5">5</string>
|
|
||||||
<string name="settings.image_loader_concurrency_6">6</string>
|
|
||||||
<string name="settings.image_loader_concurrency_7">7</string>
|
|
||||||
<string name="settings.image_loader_concurrency_8">8</string>
|
|
||||||
<string name="settings.image_loader_concurrency_9">9</string>
|
|
||||||
<string name="settings.image_loader_concurrency_10">10</string>
|
|
||||||
<string name="settings.image_loader_concurrency_11">11</string>
|
|
||||||
<string name="settings.image_loader_concurrency_12">12</string>
|
|
||||||
<string name="albumArt">albumArt</string>
|
<string name="albumArt">albumArt</string>
|
||||||
<string name="common_multiple_years">Multiple Years</string>
|
<string name="common_multiple_years">Multiple Years</string>
|
||||||
<string name="settings.server_address_unset" translatable="false">http://example.com</string>
|
<string name="settings.server_address_unset" translatable="false">http://example.com</string>
|
||||||
|
@ -506,10 +493,6 @@
|
||||||
|
|
||||||
<!-- Subsonic feature flags -->
|
<!-- Subsonic feature flags -->
|
||||||
<string name="feature_flags_category_title">Feature Flags</string>
|
<string name="feature_flags_category_title">Feature Flags</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_five_star_rating_title">Use five star rating for songs</string>
|
<string name="feature_flags_five_star_rating_title">Use five star rating for songs</string>
|
||||||
<string name="feature_flags_five_star_rating_description">Use five star rating system for songs
|
<string name="feature_flags_five_star_rating_description">Use five star rating system for songs
|
||||||
instead of simply starring/unstarring items.
|
instead of simply starring/unstarring items.
|
||||||
|
|
Loading…
Reference in New Issue