From 0989ee20a6a53eb5c7c79e850947977715850656 Mon Sep 17 00:00:00 2001 From: tzugen Date: Wed, 2 Jun 2021 20:37:13 +0200 Subject: [PATCH] Remove LegacyImageLoader, use Picasso as default --- .../subsonic/loader/image/RequestCreator.kt | 24 +- .../loader/image/SubsonicImageLoader.kt | 13 +- .../api/subsonic/SubsonicAPIClient.kt | 3 + .../api/subsonic/di/SubsonicApiModule.kt | 8 - .../ultrasonic/fragment/SettingsFragment.java | 44 +- .../provider/UltrasonicAppWidgetProvider.java | 5 +- .../org/moire/ultrasonic/util/FileUtil.java | 62 +-- .../moire/ultrasonic/util/ImageLoader.java | 21 +- .../ultrasonic/util/LegacyImageLoader.java | 450 ------------------ .../ultrasonic/activity/NavigationActivity.kt | 1 - .../ultrasonic/fragment/AlbumRowAdapter.kt | 1 - .../ultrasonic/fragment/ArtistRowAdapter.kt | 1 - .../ultrasonic/fragment/GenericRowAdapter.kt | 9 - .../ultrasonic/service/MediaPlayerService.kt | 4 +- .../ultrasonic/service/OfflineMusicService.kt | 4 +- .../ultrasonic/service/RESTMusicService.kt | 13 +- .../subsonic/ImageLoaderProvider.kt | 28 +- .../subsonic/SubsonicImageLoaderProxy.kt | 16 +- ultrasonic/src/main/res/values-cs/strings.xml | 17 - ultrasonic/src/main/res/values-de/strings.xml | 15 - ultrasonic/src/main/res/values-es/strings.xml | 17 - ultrasonic/src/main/res/values-fr/strings.xml | 17 - ultrasonic/src/main/res/values-hu/strings.xml | 17 - ultrasonic/src/main/res/values-it/strings.xml | 12 - ultrasonic/src/main/res/values-nl/strings.xml | 17 - ultrasonic/src/main/res/values-pl/strings.xml | 16 - .../src/main/res/values-pt-rBR/strings.xml | 17 - ultrasonic/src/main/res/values-pt/strings.xml | 17 - ultrasonic/src/main/res/values-ru/strings.xml | 16 - .../src/main/res/values-zh-rCN/strings.xml | 13 - ultrasonic/src/main/res/values/strings.xml | 17 - 31 files changed, 74 insertions(+), 841 deletions(-) delete mode 100644 core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/di/SubsonicApiModule.kt delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/util/LegacyImageLoader.java diff --git a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreator.kt b/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreator.kt index 78ae0b95..1e57fff9 100644 --- a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreator.kt +++ b/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreator.kt @@ -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 * 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() - .scheme(SCHEME) - .authority(AUTHORITY) - .appendPath(COVER_ART_PATH) - .appendQueryParameter(QUERY_ID, config.entityId) - .appendQueryParameter(SIZE, config.size.toString()) - .build() + .scheme(SCHEME) + .authority(AUTHORITY) + .appendPath(COVER_ART_PATH) + .appendQueryParameter(QUERY_ID, entityId) + .appendQueryParameter(SIZE, size.toString()) + .build() internal fun createLoadAvatarRequest(username: String): Uri = Uri.Builder() - .scheme(SCHEME) - .authority(AUTHORITY) - .appendPath(AVATAR_PATH) - .appendQueryParameter(QUERY_USERNAME, username) - .build() + .scheme(SCHEME) + .authority(AUTHORITY) + .appendPath(AVATAR_PATH) + .appendQueryParameter(QUERY_USERNAME, username) + .build() diff --git a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/SubsonicImageLoader.kt b/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/SubsonicImageLoader.kt index 7fa27f84..fcb2eba7 100644 --- a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/SubsonicImageLoader.kt +++ b/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/SubsonicImageLoader.kt @@ -6,9 +6,7 @@ import com.squareup.picasso.Picasso import com.squareup.picasso.RequestCreator import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient -// TODO: Caching doesn't work as expected because our query string varies. -// Need to use .stableKey() method - +// TODO: Implement OkHTTP disk caching class SubsonicImageLoader( context: Context, apiClient: SubsonicAPIClient @@ -16,7 +14,10 @@ class SubsonicImageLoader( private val picasso = Picasso.Builder(context) .addRequestHandler(CoverArtRequestHandler(apiClient)) .addRequestHandler(AvatarRequestHandler(apiClient)) - .build().apply { setIndicatorsEnabled(true) } + .build().apply { + setIndicatorsEnabled(BuildConfig.DEBUG) + Picasso.setSingletonInstance(this) + } fun load(request: ImageRequest) = when (request) { is ImageRequest.CoverArt -> loadCoverArt(request) @@ -24,10 +25,10 @@ class SubsonicImageLoader( } private fun loadCoverArt(request: ImageRequest.CoverArt) { - picasso.load(createLoadCoverArtRequest(request)) + picasso.load(createLoadCoverArtRequest(request.entityId, request.size.toLong())) .addPlaceholder(request) .addError(request) - .stableKey("${request.entityId}-${request.size}" ) + .stableKey("${request.entityId}-${request.size}") .into(request.imageView) } diff --git a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt index fb6ce161..7b3a7600 100644 --- a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt +++ b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt @@ -103,6 +103,7 @@ class SubsonicAPIClient( 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]. * * 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]. * * 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]. * * It detects the response `Content-Type` and tries to parse subsonic error if there is one. diff --git a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/di/SubsonicApiModule.kt b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/di/SubsonicApiModule.kt deleted file mode 100644 index b56dc097..00000000 --- a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/di/SubsonicApiModule.kt +++ /dev/null @@ -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()) } -} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java index fcc81219..66da521d 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/SettingsFragment.java @@ -6,6 +6,8 @@ import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; import android.provider.SearchRecentSuggestions; +import android.view.View; + import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.fragment.app.DialogFragment; @@ -18,9 +20,6 @@ import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceManager; -import timber.log.Timber; -import android.view.View; - import org.jetbrains.annotations.NotNull; import org.koin.java.KoinJavaComponent; 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.service.Consumer; import org.moire.ultrasonic.service.MediaPlayerController; -import org.moire.ultrasonic.subsonic.ImageLoaderProvider; -import org.moire.ultrasonic.util.*; +import org.moire.ultrasonic.util.Constants; +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 kotlin.Lazy; +import timber.log.Timber; import static org.koin.java.KoinJavaComponent.inject; 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 showArtistPicture; private ListPreference viewRefresh; - private ListPreference imageLoaderConcurrency; private EditTextPreference sharingDefaultDescription; private EditTextPreference sharingDefaultGreeting; private TimeSpanPreference sharingDefaultExpiration; @@ -84,7 +88,6 @@ public class SettingsFragment extends PreferenceFragmentCompat private SharedPreferences settings; private final Lazy mediaPlayerControllerLazy = inject(MediaPlayerController.class); - private final Lazy imageLoader = inject(ImageLoaderProvider.class); private final Lazy permissionUtil = inject(PermissionUtil.class); private final Lazy themeChangedEventDistributor = inject(ThemeChangedEventDistributor.class); @@ -129,7 +132,6 @@ public class SettingsFragment extends PreferenceFragmentCompat sendBluetoothAlbumArt = findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_ALBUM_ART); sendBluetoothNotifications = findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_NOTIFICATIONS); viewRefresh = findPreference(Constants.PREFERENCES_KEY_VIEW_REFRESH); - imageLoaderConcurrency = findPreference(Constants.PREFERENCES_KEY_IMAGE_LOADER_CONCURRENCY); sharingDefaultDescription = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION); sharingDefaultGreeting = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_GREETING); sharingDefaultExpiration = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION); @@ -188,8 +190,6 @@ public class SettingsFragment extends PreferenceFragmentCompat setMediaButtonsEnabled(sharedPreferences.getBoolean(key, true)); } else if (Constants.PREFERENCES_KEY_SEND_BLUETOOTH_NOTIFICATIONS.equals(key)) { 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)) { setDebugLogToFile(sharedPreferences.getBoolean(key, false)); } else if (Constants.PREFERENCES_KEY_ID3_TAGS.equals(key)) { @@ -359,21 +359,6 @@ public class SettingsFragment extends PreferenceFragmentCompat private void setupFeatureFlagsPreferences() { 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( Constants.PREFERENCES_KEY_USE_FIVE_STAR_RATING); @@ -443,7 +428,6 @@ public class SettingsFragment extends PreferenceFragmentCompat chatRefreshInterval.setSummary(chatRefreshInterval.getEntry()); directoryCacheTime.setSummary(directoryCacheTime.getEntry()); viewRefresh.setSummary(viewRefresh.getEntry()); - imageLoaderConcurrency.setSummary(imageLoaderConcurrency.getEntry()); sharingDefaultExpiration.setSummary(sharingDefaultExpiration.getText()); sharingDefaultDescription.setSummary(sharingDefaultDescription.getText()); sharingDefaultGreeting.setSummary(sharingDefaultGreeting.getText()); @@ -470,14 +454,6 @@ public class SettingsFragment extends PreferenceFragmentCompat 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) { File nomediaDir = new File(FileUtil.getUltrasonicDirectory(), ".nomedia"); diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java b/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java index acb1a193..4b2e33cc 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/provider/UltrasonicAppWidgetProvider.java @@ -9,7 +9,6 @@ import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; import android.os.Environment; -import timber.log.Timber; import android.view.KeyEvent; 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.FileUtil; +import timber.log.Timber; + /** * Widget Provider for the Ultrasonic Widgets */ @@ -159,7 +160,7 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider // Set the cover art try { - Bitmap bitmap = currentSong == null ? null : FileUtil.getAlbumArtBitmap(currentSong, 240, true); + Bitmap bitmap = currentSong == null ? null : FileUtil.getAlbumArtBitmapFromDisk(currentSong, 240, true); if (bitmap == null) { diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/FileUtil.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/FileUtil.java index 95062dee..e4b7e2cc 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/FileUtil.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/FileUtil.java @@ -23,14 +23,11 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; import android.os.Environment; +import android.os.StatFs; import android.text.TextUtils; -import kotlin.Lazy; -import timber.log.Timber; - import org.moire.ultrasonic.app.UApp; import org.moire.ultrasonic.domain.MusicDirectory; -import org.moire.ultrasonic.subsonic.ImageLoaderProvider; import java.io.File; import java.io.FileInputStream; @@ -60,7 +57,6 @@ public class FileUtil private static final List PLAYLIST_FILE_EXTENSIONS = Collections.singletonList("m3u"); private static final Pattern TITLE_WITH_TRACK = Pattern.compile("^\\d\\d-.*"); - private static final Lazy imageLoaderProvider = inject(ImageLoaderProvider.class); private static final Lazy permissionUtil = inject(PermissionUtil.class); public static File getSongFile(MusicDirectory.Entry song) @@ -118,6 +114,11 @@ public class FileUtil 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) { File albumDir = getAlbumDirectory(entry); @@ -137,6 +138,11 @@ public class FileUtil 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) { File albumArtDir = getAlbumArtDirectory(); @@ -150,24 +156,13 @@ public class FileUtil 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; File avatarFile = getAvatarFile(username); 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()) { @@ -198,15 +193,7 @@ public class FileUtil Timber.e(ex, "Exception in BitmapFactory.decodeFile()"); } - Timber.i("getAvatarBitmap %s", String.valueOf(size)); - - if (bitmap != null) - { - if (imageLoader != null) - { - imageLoader.addImageToCache(bitmap, username, size); - } - } + Timber.i("getAvatarBitmapFromDisk %s", String.valueOf(size)); return bitmap; } @@ -214,24 +201,13 @@ public class FileUtil 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; File albumArtFile = getAlbumArtFile(entry); 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()) { @@ -262,15 +238,7 @@ public class FileUtil Timber.e(ex, "Exception in BitmapFactory.decodeFile()"); } - Timber.i("getAlbumArtBitmap %s", String.valueOf(size)); - - if (bitmap != null) - { - if (imageLoader != null) - { - imageLoader.addImageToCache(bitmap, entry, size); - } - } + Timber.i("getAlbumArtBitmapFromDisk %s", String.valueOf(size)); return bitmap; } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/ImageLoader.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/ImageLoader.java index d45c67b2..e1ae5ae3 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/ImageLoader.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/ImageLoader.java @@ -1,18 +1,10 @@ package org.moire.ultrasonic.util; -import android.graphics.Bitmap; import android.view.View; + import org.moire.ultrasonic.domain.MusicDirectory; 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, boolean highQuality); @@ -22,15 +14,4 @@ public interface ImageLoader { void loadImage(View view, MusicDirectory.Entry entry, boolean large, int size, 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(); } diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/LegacyImageLoader.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/LegacyImageLoader.java deleted file mode 100644 index 47654e98..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/LegacyImageLoader.java +++ /dev/null @@ -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 . - - 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. - *

- * There should normally be only one instance of this class. - * - * @author Sindre Mehus - */ -public class LegacyImageLoader implements Runnable, ImageLoader { - private final LRUCache cache = new LRUCache<>(150); - private final BlockingQueue queue; - private int imageSizeDefault; - private final int imageSizeLarge; - private Bitmap largeUnknownImage; - private Bitmap unknownAvatarImage; - private final Context context; - private Collection 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(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."); - } - } - } -} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt index 7f8abc29..9f029926 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt @@ -217,7 +217,6 @@ class NavigationActivity : AppCompatActivity() { if (item.itemId == R.id.menu_exit) { setResult(Constants.RESULT_CLOSE_ALL) mediaPlayerController.stopJukeboxService() - imageLoaderProvider.getImageLoader().stopImageLoader() finish() exit() } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumRowAdapter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumRowAdapter.kt index e3974614..cae48837 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumRowAdapter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumRowAdapter.kt @@ -29,7 +29,6 @@ class AlbumRowAdapter( ) : GenericRowAdapter( onItemClick, onContextMenuClick, - imageLoader, onMusicFolderUpdate ) { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt index 2e312225..009b41e3 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt @@ -30,7 +30,6 @@ class ArtistRowAdapter( ) : GenericRowAdapter( onItemClick, onContextMenuClick, - imageLoader, onMusicFolderUpdate ), SectionedAdapter { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericRowAdapter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericRowAdapter.kt index 414c76d0..2a26463c 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericRowAdapter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/GenericRowAdapter.kt @@ -20,7 +20,6 @@ import androidx.recyclerview.widget.RecyclerView import org.moire.ultrasonic.R import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.domain.MusicFolder -import org.moire.ultrasonic.util.ImageLoader import org.moire.ultrasonic.view.SelectMusicFolderView /* @@ -29,7 +28,6 @@ import org.moire.ultrasonic.view.SelectMusicFolderView abstract class GenericRowAdapter( val onItemClick: (T) -> Unit, val onContextMenuClick: (MenuItem, T) -> Boolean, - private val imageLoader: ImageLoader, private val onMusicFolderUpdate: (String?) -> Unit ) : RecyclerView.Adapter() { open var itemList: List = listOf() @@ -94,13 +92,6 @@ abstract class GenericRowAdapter( } } - 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) override fun getItemCount(): Int { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt index fdc3b4cb..9a0b2938 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt @@ -478,7 +478,7 @@ class MediaPlayerService : Service() { if (currentPlaying != null) { try { val song = currentPlaying.song - val cover = FileUtil.getAlbumArtBitmap( + val cover = FileUtil.getAlbumArtBitmapFromDisk( song, Util.getMinDisplayMetric(), true ) @@ -648,7 +648,7 @@ class MediaPlayerService : Service() { // Set song title, artist and cover if possible if (song != null) { 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!!.setContentText(song.artist) notificationBuilder!!.setLargeIcon(bitmap) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt index e6eb6142..788ca2d4 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/OfflineMusicService.kt @@ -125,7 +125,7 @@ class OfflineMusicService : MusicService, KoinComponent { highQuality: Boolean ): Bitmap? { return try { - val bitmap = FileUtil.getAvatarBitmap(username, size, highQuality) + val bitmap = FileUtil.getAvatarBitmapFromDisk(username, size, highQuality) Util.scaleBitmap(bitmap, size) } catch (ignored: Exception) { null @@ -139,7 +139,7 @@ class OfflineMusicService : MusicService, KoinComponent { highQuality: Boolean ): Bitmap? { return try { - val bitmap = FileUtil.getAlbumArtBitmap(entry, size, highQuality) + val bitmap = FileUtil.getAlbumArtBitmapFromDisk(entry, size, highQuality) Util.scaleBitmap(bitmap, size) } catch (ignored: Exception) { null diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/RESTMusicService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/RESTMusicService.kt index f3968073..b2200672 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/RESTMusicService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/RESTMusicService.kt @@ -488,6 +488,9 @@ open class RESTMusicService( 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) override fun getCoverArt( entry: MusicDirectory.Entry, @@ -499,7 +502,7 @@ open class RESTMusicService( // the same song. synchronized(entry) { // Use cached file, if existing. - var bitmap = FileUtil.getAlbumArtBitmap(entry, size, highQuality) + var bitmap = FileUtil.getAlbumArtBitmapFromDisk(entry, size, highQuality) val serverScaling = isServerScalingEnabled() if (bitmap == null) { @@ -507,8 +510,9 @@ open class RESTMusicService( val id = entry.coverArt + // Can't load empty string ids if (TextUtils.isEmpty(id)) { - return null // Can't load + return null } 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) override fun getAvatar( username: String?, @@ -824,7 +831,7 @@ open class RESTMusicService( synchronized(username) { // Use cached file, if existing. - var bitmap = FileUtil.getAvatarBitmap(username, size, highQuality) + var bitmap = FileUtil.getAvatarBitmapFromDisk(username, size, highQuality) if (bitmap == null) { var inputStream: InputStream? = null diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ImageLoaderProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ImageLoaderProvider.kt index 3ee6bf53..e1907f66 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ImageLoaderProvider.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ImageLoaderProvider.kt @@ -3,11 +3,7 @@ package org.moire.ultrasonic.subsonic import android.content.Context import org.koin.core.component.KoinComponent 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.LegacyImageLoader -import org.moire.ultrasonic.util.Util /** * Handles the lifetime of the Image Loader @@ -17,33 +13,13 @@ class ImageLoaderProvider(val context: Context) : KoinComponent { @Synchronized fun clearImageLoader() { - if ( - imageLoader != null && - imageLoader!!.isRunning - ) { - imageLoader!!.clear() - } imageLoader = null } @Synchronized fun getImageLoader(): ImageLoader { - if (imageLoader == null || !imageLoader!!.isRunning) { - val legacyImageLoader = LegacyImageLoader( - 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() + if (imageLoader == null) { + imageLoader = SubsonicImageLoaderProxy(get()) } return imageLoader!! } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/SubsonicImageLoaderProxy.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/SubsonicImageLoaderProxy.kt index 2464dfcc..27f6ec0a 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/SubsonicImageLoaderProxy.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/SubsonicImageLoaderProxy.kt @@ -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.SubsonicImageLoader import org.moire.ultrasonic.util.ImageLoader -import org.moire.ultrasonic.util.LegacyImageLoader import org.moire.ultrasonic.util.Util /** - * Temporary proxy between new [SubsonicImageLoader] and [ImageLoader] interface and old - * [LegacyImageLoader] implementation. - * - * Should be removed on [LegacyImageLoader] removal. + * Proxy between [SubsonicImageLoader] and the main App. + * Needed to calculate values like the maximum image size, + * which we can't outside the main package. */ + class SubsonicImageLoaderProxy( - legacyImageLoader: LegacyImageLoader, private val subsonicImageLoader: SubsonicImageLoader -) : ImageLoader by legacyImageLoader { +) : ImageLoader { private var imageSizeLarge = Util.getMaxDisplayMetric() private var imageSizeDefault = 0 - override fun loadImage( view: View?, entry: MusicDirectory.Entry?, @@ -87,7 +84,8 @@ class SubsonicImageLoaderProxy( init { 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 // image and querying its size. diff --git a/ultrasonic/src/main/res/values-cs/strings.xml b/ultrasonic/src/main/res/values-cs/strings.xml index e1fc4b29..55ba92ac 100644 --- a/ultrasonic/src/main/res/values-cs/strings.xml +++ b/ultrasonic/src/main/res/values-cs/strings.xml @@ -379,19 +379,6 @@ Zobrazit všechny skladby umělce Přidat nový zápis v náhledu umělců pro přístup ke všem skladbám umělce Zobrazit umělce - Počet vláken stahování obrázků - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 albumArt Vícenásobné roky Pokračovat v přehrávání po připojení bluetooth přístroje @@ -501,10 +488,6 @@ Příznaky funkcí - Povolit nové načítání obrázků - 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. - Používat pět hvězdiček pro hodnocení skladeb Používat pět hvězdiček pro hodnocení skladeb namísto jednoduchého jednohvězdičkového hodnocení. diff --git a/ultrasonic/src/main/res/values-de/strings.xml b/ultrasonic/src/main/res/values-de/strings.xml index ee0c65bf..aab075fe 100644 --- a/ultrasonic/src/main/res/values-de/strings.xml +++ b/ultrasonic/src/main/res/values-de/strings.xml @@ -376,19 +376,6 @@ Alle Titel nach Künstler sortieren Einen neuen Eintrag in der Künstleransicht hinzufügen, um auf alle Lieder eines Künstlers zuzugreifen Künstler zeigen - Paralleles laden von Bildern - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 Mehrere Jahre Server hinzufügen @@ -434,8 +421,6 @@ Funktionseinstellungem - Neuen Bild-Lader aktivieren - Neuen Bild-Lader aktivieren. Bilder werden derzeit nur im Chache gespeichert. Verwenden Sie Fünf-Sterne-Bewertung für Songs Verwenden Sie Fünf-Sterne-Bewertungssystem für Songs          anstatt einfach Elemente zu markieren / zu entfernen. diff --git a/ultrasonic/src/main/res/values-es/strings.xml b/ultrasonic/src/main/res/values-es/strings.xml index 1225d7eb..35192166 100644 --- a/ultrasonic/src/main/res/values-es/strings.xml +++ b/ultrasonic/src/main/res/values-es/strings.xml @@ -393,19 +393,6 @@ Mostrar todas las canciones por artista Añadir nueva entrada en la vista de artista para acceder a todas las canciones de un artista Mostrar artista - Concurrencia del cargador de imágenes - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 Caratula del Álbum Múltiples años Reanudar al conectar un dispositivo Bluetooth @@ -503,10 +490,6 @@ Funciones experimentales - Habilitar nuevo cargador de imágenes - 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. - Use cinco estrellas para las canciones Utilice el sistema de calificación de cinco estrellas para canciones en lugar de simplemente destacar / desestimar elementos. diff --git a/ultrasonic/src/main/res/values-fr/strings.xml b/ultrasonic/src/main/res/values-fr/strings.xml index 8e9ba787..5bc3b7b7 100644 --- a/ultrasonic/src/main/res/values-fr/strings.xml +++ b/ultrasonic/src/main/res/values-fr/strings.xml @@ -381,19 +381,6 @@ Voir tous les titres par artiste Ajouter une nouvelle entrée dans la vue artiste pour accéder à toutes les titres d\'un artiste Afficher l\'artiste - Chargements d’images simultanés - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 Pochette d\'album Années multiples Reprendre lorsqu’un appareil Bluetooth se connecte @@ -491,10 +478,6 @@ Drapeaux des fonctionnalités - Activer le nouveau chargeur d\'images - 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. - Utiliser les étoiles pour noter les morceaux Utiliser un système de notation à base d\'étoiles pour les morceaux au lieu de simplement mettre en avant les morceaux. diff --git a/ultrasonic/src/main/res/values-hu/strings.xml b/ultrasonic/src/main/res/values-hu/strings.xml index c913b846..5476f2c3 100644 --- a/ultrasonic/src/main/res/values-hu/strings.xml +++ b/ultrasonic/src/main/res/values-hu/strings.xml @@ -393,19 +393,6 @@ Az előadó összes dalának megjelenítése Új bejegyzés hozzáadása az előadóhoz, az előadó összes dalának eléréséhez. Ugrás az előadóhoz - Image Loader Concurrency - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 albumArt Több év Folytatás Bluetooth eszköz csatlakozásakor @@ -501,10 +488,6 @@ Jellemzők Zászlók - Engedélyezzen új képbetöltőt - 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. - Öt csillagos értékelés használata a dalokhoz Öt csillag használata az értékeléshez az egyszerű csillaggal jelölés helyett. diff --git a/ultrasonic/src/main/res/values-it/strings.xml b/ultrasonic/src/main/res/values-it/strings.xml index 043368a8..f7031d63 100644 --- a/ultrasonic/src/main/res/values-it/strings.xml +++ b/ultrasonic/src/main/res/values-it/strings.xml @@ -338,18 +338,6 @@ Condividi canzoni via MX Player Predefinito - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 1 canzone %d canzoni diff --git a/ultrasonic/src/main/res/values-nl/strings.xml b/ultrasonic/src/main/res/values-nl/strings.xml index 2b2da416..9cd9fd00 100644 --- a/ultrasonic/src/main/res/values-nl/strings.xml +++ b/ultrasonic/src/main/res/values-nl/strings.xml @@ -393,19 +393,6 @@ Alle nummers van artiest tonen Item toevoegen in artiestweergave om alle nummers van een artiest te bekijken Artiest tonen - Aantal tegelijkertijd te laden afbeeldingen - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 Albumhoes Meerdere jaren Hervatten bij verbinding met bluetoothapparaat @@ -503,10 +490,6 @@ Experimentele functies - Nieuwe manier van afbeeldingen laden inschakelen - Schakelt de nieuwe methode voor het laden van afbeeldingen in. - Momenteel slaat het geen afbeeldingen op op de apparaatopslag en wordt alleen geheugencache gebruikt. - Gebruik vijf sterren voor nummers Gebruik vijf sterren ratingsysteem voor liedjes in plaats van items simpelweg in de hoofdrol te zetten / niet te verwijderen. diff --git a/ultrasonic/src/main/res/values-pl/strings.xml b/ultrasonic/src/main/res/values-pl/strings.xml index c09b4479..4f7b15d1 100644 --- a/ultrasonic/src/main/res/values-pl/strings.xml +++ b/ultrasonic/src/main/res/values-pl/strings.xml @@ -376,19 +376,6 @@ ponieważ api Subsonic nie wspiera nowego sposobu autoryzacji dla użytkowników Wyświetlaj wszystkie utwory artysty Dodaje nową pozycję w widoku artysty z wszystkimi jego utworami Wyświetlaj artystę - Ilość jednocześnie ładowanych obrazów - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 Okładka Z różnych lat Dodaj serwer @@ -449,9 +436,6 @@ ponieważ api Subsonic nie wspiera nowego sposobu autoryzacji dla użytkowników Flagi funkcji - Włącz program ładujący nowe obrazy - 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ą. Użyj pięciu gwiazdek dla utworów W przypadku utworów użyj systemu pięciu gwiazdek zamiast po prostu grać gwiazdkami / bez gwiazd. diff --git a/ultrasonic/src/main/res/values-pt-rBR/strings.xml b/ultrasonic/src/main/res/values-pt-rBR/strings.xml index 826a45e4..903d7b6a 100644 --- a/ultrasonic/src/main/res/values-pt-rBR/strings.xml +++ b/ultrasonic/src/main/res/values-pt-rBR/strings.xml @@ -381,19 +381,6 @@ Mostrar Todas as Músicas por Artista Adicionar nova entrada em artista para acessar todas as músicas do artista Mostrar Artista - Concorrência ao Carregar Imagens - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 albumArt Anos Múltiplos Retomar ao Conectar Dispositivo Bluetooth @@ -489,10 +476,6 @@ Bandeiras de Recursos - Ativar Novo Carregador de Imagens - 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. - Usar Classif. de 5 estrelas Para Músicas Use o sistema de classificação de 5 estrelas para músicas em vez de simplesmente estrelar/não estrelar itens. diff --git a/ultrasonic/src/main/res/values-pt/strings.xml b/ultrasonic/src/main/res/values-pt/strings.xml index 74a7d3d4..8fb5bb12 100644 --- a/ultrasonic/src/main/res/values-pt/strings.xml +++ b/ultrasonic/src/main/res/values-pt/strings.xml @@ -376,19 +376,6 @@ Todas as Músicas do Artista Adicionar nova entrada em artista para ver todas as músicas do artista Mostrar Artista - Concorrência ao Carregar Imagens - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 Múltiplos Anos Adicionar Servidor @@ -434,10 +421,6 @@ Bandeiras de recursos - Ativar novo carregador de imagens - 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. - Use classificação de cinco estrelas para músicas Use o sistema de classificação de cinco estrelas para músicas em vez de simplesmente estrelar / não estrelar itens. diff --git a/ultrasonic/src/main/res/values-ru/strings.xml b/ultrasonic/src/main/res/values-ru/strings.xml index 76d8f0c0..0897075f 100644 --- a/ultrasonic/src/main/res/values-ru/strings.xml +++ b/ultrasonic/src/main/res/values-ru/strings.xml @@ -368,19 +368,6 @@ Показать все треки исполнителя Добавить новую запись в представлении исполнителя, чтобы получить доступ ко всем песням для исполнителя Показать исполнителей - Загрузчик совпадающих изображений - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 albumArt Несколько лет @@ -396,7 +383,4 @@ Флаги - Включить новый загрузчик изображений - Включает новую реализацию загрузчика изображений. -В настоящее время он не сохраняет изображение в памяти устройства и использует только кэш в памяти. diff --git a/ultrasonic/src/main/res/values-zh-rCN/strings.xml b/ultrasonic/src/main/res/values-zh-rCN/strings.xml index 716b65f4..60067c12 100644 --- a/ultrasonic/src/main/res/values-zh-rCN/strings.xml +++ b/ultrasonic/src/main/res/values-zh-rCN/strings.xml @@ -264,18 +264,6 @@ MX Player 默认 分享 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 已禁用 删除文件 删除日志文件 @@ -307,5 +295,4 @@ 版本不兼容,请升级 Ultrasonic 应用。 不兼容的版本。请升级Subsonic 服务。 - 启用新的图像加载器 diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index f0733e73..62556828 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -395,19 +395,6 @@ Show All Songs By Artist Add new entry in artist view to access all songs for an artist Show Artist - Image Loader Concurrency - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 albumArt Multiple Years http://example.com @@ -506,10 +493,6 @@ Feature Flags - Enable new image loader - Enables new image loader implementation. - Currently it doesn\'t save image in device storage and uses only cache in memory. - Use five star rating for songs Use five star rating system for songs instead of simply starring/unstarring items.