Merge remote-tracking branch 'remotes/upstream/develop' into navigation-servers

This commit is contained in:
Nite 2021-10-15 13:52:50 +02:00
commit 477f6f5d7c
No known key found for this signature in database
GPG Key ID: 1D1AD59B1C6386C1
87 changed files with 1322 additions and 2887 deletions

View File

@ -8,4 +8,8 @@ updates:
- package-ecosystem: "gradle" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
interval: "monthly"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-patch"]

View File

@ -5,14 +5,19 @@ import java.util.Calendar
data class Album(
val id: String = "",
val parent: String = "",
val album: String = "",
val title: String = "",
val name: String = "",
val discNumber: Int = 0,
val coverArt: String = "",
val songCount: Int = 0,
val created: Calendar? = null,
val artist: String = "",
val artistId: String = "",
val songCount: Int = 0,
val duration: Int = 0,
val created: Calendar? = null,
val year: Int = 0,
val genre: String = "",
@JsonProperty("song") val songList: List<MusicDirectoryChild> = emptyList()
@JsonProperty("song") val songList: List<MusicDirectoryChild> = emptyList(),
@JsonProperty("starred") val starredDate: String = ""
)

View File

@ -3,13 +3,13 @@ ext.versions = [
targetSdk : 29,
compileSdk : 29,
// You need to run ./gradlew wrapper after updating the version
gradle : '7.0',
gradle : '7.2',
navigation : "2.3.5",
gradlePlugin : "4.2.2",
androidxcore : "1.5.0",
ktlint : "0.37.1",
ktlintGradle : "9.2.1",
ktlintGradle : "10.2.0",
detekt : "1.18.1",
jacoco : "0.8.7",
preferences : "1.1.1",

View File

@ -0,0 +1,2 @@
Bug fixes
- #571: Fixed media session null checks.

View File

@ -0,0 +1,15 @@
Bug fixes
- #594: Added PlaybackComplete intent when a song playback is completed.
- #593: Fixed album lists.
- #602: Fix NPE.
Enhancements
- #558: Video call can be static.
- #559: Add better offline Support.
- #568: Rework Downloader.
- #567: Use semantically correct API endpoint when streaming/downloading.
- #572: Moved drag handle to the left in the Now Playing list.
- #585: Added setting to disable Now Playing List sending for incompatible
bluetooth devices.
- #596: Added option whether to create a share on the server when sharing
songs.

View File

@ -0,0 +1,2 @@
Correción de errores
- #571: Se comprueban los valores nulos en la sesión de medios.

View File

@ -0,0 +1,18 @@
Correción de errores
- #594: Agregado un intent de PlaybackComplete cuando se completa la
reproducción de una canción.
- #593: Corregidas las listas de álbumes.
- #602: NPE corregido.
Mejoras
- #558: La llamada a video puede ser estática.
- #559: Agregado un mejor soporte sin conexión.
- #568: Se ha reescrito el downloader.
- #567: Se utiliza el endpoint semánticamente correcto al realizar streaming
o descargar.
- #572: Se ha movido el botón de arrastre de canción hacia la izquierda en
la lista de reproducción.
- #585: Agregada una configuración para deshabilitar el envío de la Lista de
reproducción en curso para dispositivos Bluetooth incompatibles.
- #596: Se agregó la opción de crear un recurso compartido en el servidor al
compartir canciones.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -32,9 +32,9 @@ ext {
jacocoTestReport {
reports {
html.enabled true
csv.enabled false
xml.enabled true
html.required = true
xml.required = false
csv.required = false
}
afterEvaluate {

View File

@ -9,8 +9,8 @@ android {
defaultConfig {
applicationId "org.moire.ultrasonic"
versionCode 95
versionName "2.23.0"
versionCode 98
versionName "2.24.0"
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,9 @@
android:theme="@style/NoActionBar"
android:name=".app.UApp"
android:label="@string/common.appname"
android:usesCleartextTraffic="true">
android:usesCleartextTraffic="true"
android:supportsRtl="false"
tools:ignore="UnusedAttribute">
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>

View File

@ -141,7 +141,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
sharingDefaultGreeting.setText(Settings.getShareGreeting());
setupClearSearchPreference();
setupGaplessControlSettingsV14();
setupFeatureFlagsPreferences();
setupCacheLocationPreference();
setupBluetoothDevicePreferences();
@ -221,8 +220,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
}
private void setupCacheLocationPreference() {
cacheLocation.setSummary(settings.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION,
FileUtil.getDefaultMusicDirectory().getPath()));
cacheLocation.setSummary(Settings.getCacheLocation());
cacheLocation.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
@ -373,24 +371,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
}
private void setupGaplessControlSettingsV14() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
PreferenceCategory playbackControlSettings =
findPreference(Constants.PREFERENCES_KEY_PLAYBACK_CONTROL_SETTINGS);
CheckBoxPreference gaplessPlaybackEnabled =
findPreference(Constants.PREFERENCES_KEY_GAPLESS_PLAYBACK);
if (gaplessPlaybackEnabled != null) {
gaplessPlaybackEnabled.setChecked(false);
gaplessPlaybackEnabled.setEnabled(false);
if (playbackControlSettings != null) {
playbackControlSettings.removePreference(gaplessPlaybackEnabled);
}
}
}
}
private void update() {
theme.setSummary(theme.getEntry());
maxBitrateWifi.setSummary(maxBitrateWifi.getEntry());
@ -412,8 +392,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
sharingDefaultExpiration.setSummary(sharingDefaultExpiration.getText());
sharingDefaultDescription.setSummary(sharingDefaultDescription.getText());
sharingDefaultGreeting.setSummary(sharingDefaultGreeting.getText());
cacheLocation.setSummary(settings.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION,
FileUtil.getDefaultMusicDirectory().getPath()));
cacheLocation.setSummary(Settings.getCacheLocation());
if (!mediaButtonsEnabled.isChecked()) {
lockScreenEnabled.setChecked(false);

View File

@ -1,233 +0,0 @@
package org.moire.ultrasonic.util;
import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import timber.log.Timber;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.PermissionChecker;
import com.karumi.dexter.Dexter;
import com.karumi.dexter.MultiplePermissionsReport;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.DexterError;
import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.PermissionRequestErrorListener;
import com.karumi.dexter.listener.multi.MultiplePermissionsListener;
import org.moire.ultrasonic.R;
import java.util.List;
import static androidx.core.content.PermissionChecker.PERMISSION_DENIED;
/**
* Contains static functions for Permission handling
*/
public class PermissionUtil {
private Context activityContext;
private final Context applicationContext;
public PermissionUtil(Context context) {
applicationContext = context;
}
public interface PermissionRequestFinishedCallback {
void onPermissionRequestFinished(boolean hasPermission);
}
public void ForegroundApplicationStarted(Context context) {
this.activityContext = context;
}
public void ForegroundApplicationStopped() {
activityContext = null;
}
/**
* This function can be used to handle file access permission failures.
*
* It will check if the failure is because the necessary permissions aren't available,
* and it will request them, if necessary.
*
* @param callback callback function to execute after the permission request is finished
*/
public void handlePermissionFailed(final PermissionRequestFinishedCallback callback) {
String currentCachePath = Settings.getPreferences().getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, FileUtil.getDefaultMusicDirectory().getPath());
String defaultCachePath = FileUtil.getDefaultMusicDirectory().getPath();
// Ultrasonic can do nothing about this error when the Music Directory is already set to the default.
if (currentCachePath.compareTo(defaultCachePath) == 0) return;
if ((PermissionChecker.checkSelfPermission(applicationContext, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PERMISSION_DENIED) ||
(PermissionChecker.checkSelfPermission(applicationContext, Manifest.permission.READ_EXTERNAL_STORAGE) == PERMISSION_DENIED)) {
// While we request permission, the Music Directory is temporarily reset to its default location
setCacheLocation(applicationContext, FileUtil.getDefaultMusicDirectory().getPath());
// If the application is not running, we can't notify the user
if (activityContext == null) return;
requestFailedPermission(activityContext, currentCachePath, callback);
} else {
setCacheLocation(applicationContext, FileUtil.getDefaultMusicDirectory().getPath());
// If the application is not running, we can't notify the user
if (activityContext != null) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
showWarning(activityContext, activityContext.getString(R.string.permissions_message_box_title), activityContext.getString(R.string.permissions_access_error), null);
}
});
}
callback.onPermissionRequestFinished(false);
}
}
/**
* This function requests permission to access the filesystem.
* It can be used to request the permission initially, e.g. when the user decides to use a non-default folder for the cache
* @param context context for the operation
* @param callback callback function to execute after the permission request is finished
*/
public static void requestInitialPermission(final Context context, final PermissionRequestFinishedCallback callback) {
Dexter.withContext(context)
.withPermissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE)
.withListener(new MultiplePermissionsListener() {
@Override
public void onPermissionsChecked(MultiplePermissionsReport report) {
if (report.areAllPermissionsGranted()) {
Timber.i("Permission granted to read / write external storage");
if (callback != null) callback.onPermissionRequestFinished(true);
return;
}
if (report.isAnyPermissionPermanentlyDenied()) {
Timber.i("Found permanently denied permission to read / write external storage, offering settings");
showSettingsDialog(context);
if (callback != null) callback.onPermissionRequestFinished(false);
return;
}
Timber.i("At least one permission is missing to read / write external storage");
showWarning(context, context.getString(R.string.permissions_message_box_title),
context.getString(R.string.permissions_rationale_description_initial), null);
if (callback != null) callback.onPermissionRequestFinished(false);
}
@Override
public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
showWarning(context, context.getString(R.string.permissions_rationale_title),
context.getString(R.string.permissions_rationale_description_initial), token);
}
}).withErrorListener(new PermissionRequestErrorListener() {
@Override
public void onError(DexterError error) {
Timber.e("An error has occurred during checking permissions with Dexter: %s", error.toString());
}
})
.check();
}
private static void setCacheLocation(Context context, String cacheLocation) {
Settings.getPreferences().edit()
.putString(Constants.PREFERENCES_KEY_CACHE_LOCATION, cacheLocation)
.apply();
}
private static void requestFailedPermission(final Context context, final String cacheLocation, final PermissionRequestFinishedCallback callback) {
Dexter.withContext(context)
.withPermissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE)
.withListener(new MultiplePermissionsListener() {
@Override
public void onPermissionsChecked(MultiplePermissionsReport report) {
if (report.areAllPermissionsGranted()) {
Timber.i("Permission granted to use cache directory %s", cacheLocation);
setCacheLocation(context, cacheLocation);
if (callback != null) callback.onPermissionRequestFinished(true);
return;
}
if (report.isAnyPermissionPermanentlyDenied()) {
Timber.i("Found permanently denied permission to use cache directory %s, offering settings", cacheLocation);
showSettingsDialog(context);
if (callback != null) callback.onPermissionRequestFinished(false);
return;
}
Timber.i("At least one permission is missing to use directory %s ", cacheLocation);
setCacheLocation(context, FileUtil.getDefaultMusicDirectory().getPath());
showWarning(context, context.getString(R.string.permissions_message_box_title),
context.getString(R.string.permissions_permission_missing), null);
if (callback != null) callback.onPermissionRequestFinished(false);
}
@Override
public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) {
showWarning(context, context.getString(R.string.permissions_rationale_title),
context.getString(R.string.permissions_rationale_description_failed), token);
}
}).withErrorListener(new PermissionRequestErrorListener() {
@Override
public void onError(DexterError error) {
Timber.e("An error has occurred during checking permissions with Dexter: %s", error.toString());
}
})
.check();
}
private static void showSettingsDialog(final Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.Theme_AppCompat_Dialog);
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setTitle(context.getString(R.string.permissions_permanent_denial_title));
builder.setMessage(context.getString(R.string.permissions_permanent_denial_description));
builder.setPositiveButton(context.getString(R.string.permissions_open_settings), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
openSettings(context);
}
});
builder.setNegativeButton(context.getString(R.string.common_cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
setCacheLocation(context, FileUtil.getDefaultMusicDirectory().getPath());
dialog.cancel();
}
});
builder.show();
}
private static void openSettings(Context context) {
Intent i = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
i.addCategory(Intent.CATEGORY_DEFAULT);
i.setData(Uri.parse("package:" + context.getPackageName()));
context.startActivity(i);
}
private static void showWarning(Context context, String title, String text, final PermissionToken token) {
AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.Theme_AppCompat_Dialog);
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setTitle(title);
builder.setMessage(text);
builder.setPositiveButton(context.getString(R.string.common_ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
if (token != null) token.continuePermissionRequest();
}
});
builder.show();
}
}

View File

@ -10,6 +10,7 @@ import java.util.List;
public class ShareDetails
{
public String Description;
public boolean ShareOnServer;
public long Expiration;
public List<MusicDirectory.Entry> Entries;
}

View File

@ -88,7 +88,7 @@ class NavigationActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
setUncaughtExceptionHandler()
permissionUtil.ForegroundApplicationStarted(this)
permissionUtil.onForegroundApplicationStarted(this)
Util.applyTheme(this)
super.onCreate(savedInstanceState)
@ -219,7 +219,7 @@ class NavigationActivity : AppCompatActivity() {
nowPlayingEventDistributor.unsubscribe(nowPlayingEventListener)
themeChangedEventDistributor.unsubscribe(themeChangedEventListener)
imageLoaderProvider.clearImageLoader()
permissionUtil.ForegroundApplicationStopped()
permissionUtil.onForegroundApplicationStopped()
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
@ -332,12 +332,7 @@ class NavigationActivity : AppCompatActivity() {
PreferenceManager.setDefaultValues(this, R.xml.settings, false)
val preferences = Settings.preferences
if (!preferences.contains(Constants.PREFERENCES_KEY_CACHE_LOCATION)) {
val editor = preferences.edit()
editor.putString(
Constants.PREFERENCES_KEY_CACHE_LOCATION,
FileUtil.defaultMusicDirectory.path
)
editor.apply()
Settings.cacheLocation = FileUtil.defaultMusicDirectory.path
}
}

View File

@ -16,7 +16,8 @@ fun Album.toDomainEntity(): MusicDirectory.Entry = MusicDirectory.Entry(
duration = this@toDomainEntity.duration,
created = this@toDomainEntity.created?.time,
year = this@toDomainEntity.year,
genre = this@toDomainEntity.genre
genre = this@toDomainEntity.genre,
starred = this@toDomainEntity.starredDate.isNotEmpty()
)
fun Album.toMusicDirectoryDomainEntity(): MusicDirectory = MusicDirectory().apply {

View File

@ -63,7 +63,8 @@ class AlbumListFragment : GenericListFragment<MusicDirectory.Entry, AlbumRowAdap
{ entry -> onItemClick(entry) },
{ menuItem, entry -> onContextMenuItemSelected(menuItem, entry) },
imageLoaderProvider.getImageLoader(),
onMusicFolderUpdate
onMusicFolderUpdate,
requireContext()
)
}

View File

@ -7,15 +7,22 @@
package org.moire.ultrasonic.fragment
import android.content.Context
import android.graphics.drawable.Drawable
import android.view.MenuItem
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import java.lang.Exception
import org.moire.ultrasonic.R
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.imageloader.ImageLoader
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
import org.moire.ultrasonic.util.Settings.shouldUseId3Tags
import org.moire.ultrasonic.util.Util
import timber.log.Timber
/**
* Creates a Row in a RecyclerView which contains the details of an Album
@ -25,13 +32,19 @@ class AlbumRowAdapter(
onItemClick: (MusicDirectory.Entry) -> Unit,
onContextMenuClick: (MenuItem, MusicDirectory.Entry) -> Boolean,
private val imageLoader: ImageLoader,
onMusicFolderUpdate: (String?) -> Unit
onMusicFolderUpdate: (String?) -> Unit,
context: Context,
) : GenericRowAdapter<MusicDirectory.Entry>(
onItemClick,
onContextMenuClick,
onMusicFolderUpdate
) {
private val starDrawable: Drawable =
Util.getDrawableFromAttribute(context, R.attr.star_full)
private val starHollowDrawable: Drawable =
Util.getDrawableFromAttribute(context, R.attr.star_hollow)
override var itemList = albumList
// Set our layout files
@ -53,6 +66,8 @@ class AlbumRowAdapter(
holder.details.setOnClickListener { onItemClick(entry) }
holder.details.setOnLongClickListener { view -> createPopupMenu(view, listPosition) }
holder.coverArtId = entry.coverArt
holder.star.setImageDrawable(if (entry.starred) starDrawable else starHollowDrawable)
holder.star.setOnClickListener { onStarClick(entry, holder.star) }
imageLoader.loadImage(
holder.coverArt, entry,
@ -78,6 +93,7 @@ class AlbumRowAdapter(
var artist: TextView = view.findViewById(R.id.album_artist)
var details: LinearLayout = view.findViewById(R.id.row_album_details)
var coverArt: ImageView = view.findViewById(R.id.album_coverart)
var star: ImageView = view.findViewById(R.id.album_star)
var coverArtId: String? = null
}
@ -87,4 +103,33 @@ class AlbumRowAdapter(
override fun newViewHolder(view: View): RecyclerView.ViewHolder {
return ViewHolder(view)
}
/**
* Handles the star / unstar action for an album
*/
private fun onStarClick(entry: MusicDirectory.Entry, star: ImageView) {
entry.starred = !entry.starred
star.setImageDrawable(if (entry.starred) starDrawable else starHollowDrawable)
val musicService = getMusicService()
Thread {
val useId3 = shouldUseId3Tags
try {
if (entry.starred) {
musicService.star(
if (!useId3) entry.id else null,
if (useId3) entry.id else null,
null
)
} else {
musicService.unstar(
if (!useId3) entry.id else null,
if (useId3) entry.id else null,
null
)
}
} catch (all: Exception) {
Timber.e(all)
}
}.start()
}
}

View File

@ -14,7 +14,13 @@ import androidx.navigation.fragment.findNavController
import com.google.android.material.switchmaterial.SwitchMaterial
import com.google.android.material.textfield.TextInputLayout
import com.skydoves.colorpickerview.ColorPickerDialog
import com.skydoves.colorpickerview.flag.BubbleFlag
import com.skydoves.colorpickerview.flag.FlagMode
import com.skydoves.colorpickerview.listeners.ColorEnvelopeListener
import java.io.IOException
import java.net.MalformedURLException
import java.net.URL
import java.util.Locale
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.moire.ultrasonic.BuildConfig
@ -32,18 +38,12 @@ import org.moire.ultrasonic.service.MusicServiceFactory
import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.ErrorDialog
import org.moire.ultrasonic.util.ModalBackgroundTask
import org.moire.ultrasonic.util.ServerColor
import org.moire.ultrasonic.util.Util
import retrofit2.Response
import timber.log.Timber
import java.io.IOException
import java.net.MalformedURLException
import java.net.URL
import java.util.*
import com.skydoves.colorpickerview.flag.FlagMode
import com.skydoves.colorpickerview.flag.BubbleFlag
import org.moire.ultrasonic.util.ServerColor
private const val DIALOG_PADDING = 12
/**
* Displays a form where server settings can be created / edited
@ -170,14 +170,18 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
this.colorPickerView.setFlagView(bubbleFlag)
}
.attachAlphaSlideBar(false)
.setPositiveButton(getString(R.string.common_ok),
.setPositiveButton(
getString(R.string.common_ok),
ColorEnvelopeListener { envelope, _ ->
selectedColor = envelope.color
updateColor(envelope.color) })
updateColor(envelope.color)
}
)
.setNegativeButton(getString(R.string.common_cancel)) {
dialogInterface, i -> dialogInterface.dismiss()
dialogInterface, i ->
dialogInterface.dismiss()
}
.setBottomSpace(12)
.setBottomSpace(DIALOG_PADDING)
.show()
}
}
@ -466,10 +470,9 @@ class EditServerFragment : Fragment(), OnBackPressedHandler {
}
Util.showDialog(
activity,
android.R.drawable.ic_dialog_info,
R.string.settings_testing_ok,
dialogText
context = requireActivity(),
titleId = R.string.settings_testing_ok,
message = dialogText
)
}

View File

@ -33,6 +33,7 @@ import android.widget.SeekBar
import android.widget.SeekBar.OnSeekBarChangeListener
import android.widget.TextView
import android.widget.ViewFlipper
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import com.mobeta.android.dslv.DragSortListView
@ -153,7 +154,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
return inflater.inflate(R.layout.current_playing, container, false)
}
fun findViews(view: View) {
private fun findViews(view: View) {
playlistFlipper = view.findViewById(R.id.current_playing_playlist_flipper)
emptyTextView = view.findViewById(R.id.playlist_empty)
songTitleTextView = view.findViewById(R.id.current_playing_song)
@ -209,7 +210,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
val nextButton: AutoRepeatButton = view.findViewById(R.id.button_next)
val shuffleButton = view.findViewById<View>(R.id.button_shuffle)
val ratingLinearLayout = view.findViewById<LinearLayout>(R.id.song_rating)
if (!useFiveStarRating) ratingLinearLayout.visibility = View.GONE
if (!useFiveStarRating) ratingLinearLayout.isVisible = false
hollowStar = Util.getDrawableFromAttribute(view.context, R.attr.star_hollow)
fullStar = Util.getDrawableFromAttribute(context, R.attr.star_full)
@ -375,7 +376,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
mediaPlayerController.isShufflePlayEnabled = true
}
visualizerViewLayout.visibility = View.GONE
visualizerViewLayout.isVisible = false
VisualizerController.get().observe(
requireActivity(),
{ visualizerController ->
@ -389,11 +390,9 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
LinearLayout.LayoutParams.MATCH_PARENT
)
)
if (!visualizerView.isActive) {
visualizerViewLayout.visibility = View.GONE
} else {
visualizerViewLayout.visibility = View.VISIBLE
}
visualizerViewLayout.isVisible = visualizerView.isActive
visualizerView.setOnTouchListener { _, _ ->
visualizerView.isActive = !visualizerView.isActive
mediaPlayerController.showVisualization = visualizerView.isActive
@ -402,7 +401,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
isVisualizerAvailable = true
} else {
Timber.d("VisualizerController Observer.onChanged has no controller")
visualizerViewLayout.visibility = View.GONE
visualizerViewLayout.isVisible = false
isVisualizerAvailable = false
}
}
@ -497,6 +496,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
val equalizerMenuItem = menu.findItem(R.id.menu_item_equalizer)
val visualizerMenuItem = menu.findItem(R.id.menu_item_visualizer)
val shareMenuItem = menu.findItem(R.id.menu_item_share)
val shareSongMenuItem = menu.findItem(R.id.menu_item_share_song)
starMenuItem = menu.findItem(R.id.menu_item_star)
val bookmarkMenuItem = menu.findItem(R.id.menu_item_bookmark_set)
val bookmarkRemoveMenuItem = menu.findItem(R.id.menu_item_bookmark_delete)
@ -523,20 +523,27 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
}
val mediaPlayerController = mediaPlayerController
val downloadFile = mediaPlayerController.currentPlaying
if (downloadFile != null) {
currentSong = downloadFile.song
}
if (useFiveStarRating) starMenuItem.isVisible = false
if (currentSong != null) {
starMenuItem.icon = if (currentSong!!.starred) fullStar else hollowStar
shareSongMenuItem.isVisible = true
} else {
starMenuItem.icon = hollowStar
shareSongMenuItem.isVisible = false
}
if (mediaPlayerController.keepScreenOn) {
screenOption?.setTitle(R.string.download_menu_screen_off)
} else {
screenOption?.setTitle(R.string.download_menu_screen_on)
}
if (jukeboxOption != null) {
jukeboxOption.isEnabled = jukeboxAvailable
jukeboxOption.isVisible = jukeboxAvailable
@ -598,9 +605,8 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
when (menuItemId) {
R.id.menu_show_artist -> {
if (entry == null) {
return false
}
if (entry == null) return false
if (Settings.shouldUseId3Tags) {
bundle = Bundle()
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, entry.artistId)
@ -613,9 +619,8 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
return true
}
R.id.menu_show_album -> {
if (entry == null) {
return false
}
if (entry == null) return false
val albumId = if (Settings.shouldUseId3Tags) entry.albumId else entry.parent
bundle = Bundle()
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, albumId)
@ -627,9 +632,8 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
return true
}
R.id.menu_lyrics -> {
if (entry == null) {
return false
}
if (entry == null) return false
bundle = Bundle()
bundle.putString(Constants.INTENT_EXTRA_NAME_ARTIST, entry.artist)
bundle.putString(Constants.INTENT_EXTRA_NAME_TITLE, entry.title)
@ -664,11 +668,9 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
R.id.menu_item_visualizer -> {
val active = !visualizerView.isActive
visualizerView.isActive = active
if (!visualizerView.isActive) {
visualizerViewLayout.visibility = View.GONE
} else {
visualizerViewLayout.visibility = View.VISIBLE
}
visualizerViewLayout.isVisible = visualizerView.isActive
mediaPlayerController.showVisualization = visualizerView.isActive
Util.toast(
context,
@ -705,9 +707,8 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
return true
}
R.id.menu_item_star -> {
if (currentSong == null) {
return true
}
if (currentSong == null) return true
val isStarred = currentSong!!.starred
val id = currentSong!!.id
if (isStarred) {
@ -732,9 +733,8 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
return true
}
R.id.menu_item_bookmark_set -> {
if (currentSong == null) {
return true
}
if (currentSong == null) return true
val songId = currentSong!!.id
val playerPosition = mediaPlayerController.playerPosition
currentSong!!.bookmarkPosition = playerPosition
@ -755,9 +755,8 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
return true
}
R.id.menu_item_bookmark_delete -> {
if (currentSong == null) {
return true
}
if (currentSong == null) return true
val bookmarkSongId = currentSong!!.id
currentSong!!.bookmarkPosition = 0
Thread {
@ -782,6 +781,15 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
shareHandler.createShare(this, entries, null, cancellationToken)
return true
}
R.id.menu_item_share_song -> {
if (currentSong == null) return true
val entries: MutableList<MusicDirectory.Entry?> = ArrayList()
entries.add(currentSong)
shareHandler.createShare(this, entries, null, cancellationToken)
return true
}
else -> return false
}
}
@ -903,7 +911,9 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
onCurrentChanged()
}
})
emptyTextView.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE
emptyTextView.isVisible = list.isEmpty()
currentRevision = mediaPlayerController.playListUpdateRevision
when (mediaPlayerController.repeatMode) {
RepeatMode.OFF -> repeatButton.setImageDrawable(
@ -1028,19 +1038,19 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon
when (playerState) {
PlayerState.STARTED -> {
pauseButton.visibility = View.VISIBLE
stopButton.visibility = View.GONE
startButton.visibility = View.GONE
pauseButton.isVisible = true
stopButton.isVisible = false
startButton.isVisible = false
}
PlayerState.DOWNLOADING, PlayerState.PREPARING -> {
pauseButton.visibility = View.GONE
stopButton.visibility = View.VISIBLE
startButton.visibility = View.GONE
pauseButton.isVisible = false
stopButton.isVisible = true
startButton.isVisible = false
}
else -> {
pauseButton.visibility = View.GONE
stopButton.visibility = View.GONE
startButton.visibility = View.VISIBLE
pauseButton.isVisible = false
stopButton.isVisible = false
startButton.isVisible = true
}
}

View File

@ -12,7 +12,6 @@ import android.widget.BaseAdapter
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.PopupMenu
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat

View File

@ -485,11 +485,7 @@ class LocalMediaPlayer : KoinComponent {
try {
setNextPlayerState(PlayerState.PREPARED)
if (Settings.gaplessPlayback &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
(
playerState === PlayerState.STARTED ||
playerState === PlayerState.PAUSED
)
(playerState === PlayerState.STARTED || playerState === PlayerState.PAUSED)
) {
mediaPlayer.setNextMediaPlayer(nextMediaPlayer)
nextSetup = true
@ -536,6 +532,7 @@ class LocalMediaPlayer : KoinComponent {
wakeLock.acquire(60000)
val pos = cachedPosition
Timber.i("Ending position %d of %d", pos, duration)
if (!isPartial || downloadFile.isWorkDone && abs(duration - pos) < 1000) {
setPlayerState(PlayerState.COMPLETED)
if (Settings.gaplessPlayback &&
@ -555,6 +552,7 @@ class LocalMediaPlayer : KoinComponent {
}
return
}
synchronized(this) {
if (downloadFile.isWorkDone) {
// Complete was called early even though file is fully buffered

View File

@ -7,6 +7,8 @@ import android.view.LayoutInflater
import android.view.View
import android.widget.CheckBox
import android.widget.EditText
import android.widget.TextView
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import java.util.Locale
@ -31,9 +33,12 @@ import org.moire.ultrasonic.util.TimeSpanPicker
class ShareHandler(val context: Context) {
private var shareDescription: EditText? = null
private var timeSpanPicker: TimeSpanPicker? = null
private var shareOnServerCheckBox: CheckBox? = null
private var hideDialogCheckBox: CheckBox? = null
private var noExpirationCheckBox: CheckBox? = null
private var saveAsDefaultsCheckBox: CheckBox? = null
private var textViewComment: TextView? = null
private var textViewExpiration: TextView? = null
private val pattern = Pattern.compile(":")
fun createShare(
@ -62,15 +67,17 @@ class ShareHandler(val context: Context) {
swipe: SwipeRefreshLayout?,
cancellationToken: CancellationToken
) {
val task: BackgroundTask<Share> = object : FragmentBackgroundTask<Share>(
val task: BackgroundTask<Share?> = object : FragmentBackgroundTask<Share?>(
fragment.requireActivity(),
true,
swipe,
cancellationToken
) {
@Throws(Throwable::class)
override fun doInBackground(): Share {
override fun doInBackground(): Share? {
val ids: MutableList<String> = ArrayList()
if (!shareDetails.ShareOnServer && shareDetails.Entries.size == 1) return null
if (shareDetails.Entries.isEmpty()) {
fragment.arguments?.getString(Constants.INTENT_EXTRA_NAME_ID)?.let {
ids.add(it)
@ -80,23 +87,51 @@ class ShareHandler(val context: Context) {
ids.add(id)
}
}
val musicService = getMusicService()
var timeInMillis: Long = 0
if (shareDetails.Expiration != 0L) {
timeInMillis = shareDetails.Expiration
}
val shares =
musicService.createShare(ids, shareDetails.Description, timeInMillis)
return shares[0]
}
override fun done(result: Share) {
override fun done(result: Share?) {
val intent = Intent(Intent.ACTION_SEND)
intent.type = "text/plain"
intent.putExtra(
Intent.EXTRA_TEXT,
String.format(Locale.ROOT, "%s\n\n%s", Settings.shareGreeting, result.url)
)
if (result != null) {
// Created a share, send the URL
intent.putExtra(
Intent.EXTRA_TEXT,
String.format(
Locale.ROOT, "%s\n\n%s", Settings.shareGreeting, result.url
)
)
} else {
// Sending only text details
val textBuilder = StringBuilder()
textBuilder.appendLine(Settings.shareGreeting)
if (!shareDetails.Entries[0].title.isNullOrEmpty())
textBuilder.append(context.resources.getString(R.string.common_title))
.append(": ").appendLine(shareDetails.Entries[0].title)
if (!shareDetails.Entries[0].artist.isNullOrEmpty())
textBuilder.append(context.resources.getString(R.string.common_artist))
.append(": ").appendLine(shareDetails.Entries[0].artist)
if (!shareDetails.Entries[0].album.isNullOrEmpty())
textBuilder.append(context.resources.getString(R.string.common_album))
.append(": ").append(shareDetails.Entries[0].album)
intent.putExtra(Intent.EXTRA_TEXT, textBuilder.toString())
}
fragment.activity?.startActivity(
Intent.createChooser(
intent,
@ -119,24 +154,45 @@ class ShareHandler(val context: Context) {
if (layout != null) {
shareDescription = layout.findViewById<View>(R.id.share_description) as EditText
hideDialogCheckBox = layout.findViewById<View>(R.id.hide_dialog) as CheckBox
shareOnServerCheckBox = layout.findViewById<View>(R.id.share_on_server) as CheckBox
noExpirationCheckBox = layout.findViewById<View>(
R.id.timeSpanDisableCheckBox
) as CheckBox
saveAsDefaultsCheckBox = layout.findViewById<View>(R.id.save_as_defaults) as CheckBox
timeSpanPicker = layout.findViewById<View>(R.id.date_picker) as TimeSpanPicker
textViewComment = layout.findViewById<View>(R.id.textViewComment) as TextView
textViewExpiration = layout.findViewById<View>(R.id.textViewExpiration) as TextView
}
if (shareDetails.Entries.size == 1) {
// For single songs the sharing may be done by text only
shareOnServerCheckBox?.setOnCheckedChangeListener { _, _ ->
updateVisibility()
}
shareOnServerCheckBox?.isChecked = Settings.shareOnServer
} else {
shareOnServerCheckBox?.isVisible = false
}
updateVisibility()
val builder = AlertDialog.Builder(fragment.context)
builder.setTitle(R.string.share_set_share_options)
builder.setPositiveButton(R.string.common_save) { _, _ ->
builder.setPositiveButton(R.string.menu_share) { _, _ ->
if (!noExpirationCheckBox!!.isChecked) {
val timeSpan: TimeSpan = timeSpanPicker!!.timeSpan
val now = TimeSpan.getCurrentTime()
shareDetails.Expiration = now.add(timeSpan).totalMilliseconds
}
shareDetails.Description = shareDescription!!.text.toString()
shareDetails.ShareOnServer = shareOnServerCheckBox!!.isChecked
if (hideDialogCheckBox!!.isChecked) {
Settings.shouldAskForShareDetails = false
}
if (saveAsDefaultsCheckBox!!.isChecked) {
val timeSpanType: String = timeSpanPicker!!.timeSpanType
val timeSpanAmount: Int = timeSpanPicker!!.timeSpanAmount
@ -145,22 +201,29 @@ class ShareHandler(val context: Context) {
String.format("%d:%s", timeSpanAmount, timeSpanType) else ""
Settings.defaultShareDescription = shareDetails.Description
Settings.shareOnServer = shareDetails.ShareOnServer
}
share(fragment, shareDetails, swipe, cancellationToken)
}
builder.setNegativeButton(R.string.common_cancel) { dialog, _ ->
dialog.cancel()
}
builder.setView(layout)
builder.setCancelable(true)
timeSpanPicker!!.setTimeSpanDisableText(context.resources.getString(R.string.no_expiration))
noExpirationCheckBox!!.setOnCheckedChangeListener {
_,
b ->
timeSpanPicker!!.isEnabled = !b
}
val defaultDescription = Settings.defaultShareDescription
val timeSpan = Settings.defaultShareExpiration
val split = pattern.split(timeSpan)
if (split.size == 2) {
val timeSpanAmount = split[0].toInt()
@ -178,8 +241,25 @@ class ShareHandler(val context: Context) {
noExpirationCheckBox!!.isChecked = true
timeSpanPicker!!.isEnabled = false
}
shareDescription!!.setText(defaultDescription)
builder.create()
builder.show()
}
private fun updateVisibility() {
if (!shareOnServerCheckBox!!.isVisible || shareOnServerCheckBox!!.isChecked) {
noExpirationCheckBox?.isVisible = true
timeSpanPicker?.isVisible = true
shareDescription?.isVisible = true
textViewComment?.isVisible = true
textViewExpiration?.isVisible = true
} else {
noExpirationCheckBox?.isVisible = false
timeSpanPicker?.isVisible = false
shareDescription?.isVisible = false
textViewComment?.isVisible = false
textViewExpiration?.isVisible = false
}
}
}

View File

@ -104,6 +104,7 @@ object Constants {
const val PREFERENCES_KEY_ASK_FOR_SHARE_DETAILS = "sharingAlwaysAskForDetails"
const val PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION = "sharingDefaultDescription"
const val PREFERENCES_KEY_DEFAULT_SHARE_GREETING = "sharingDefaultGreeting"
const val PREFERENCES_KEY_SHARE_ON_SERVER = "sharingCreateOnServer"
const val PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION = "sharingDefaultExpiration"
const val PREFERENCES_KEY_SHOW_ALL_SONGS_BY_ARTIST = "showAllSongsByArtist"
const val PREFERENCES_KEY_USE_FIVE_STAR_RATING = "use_five_star_rating"

View File

@ -253,9 +253,8 @@ object FileUtil {
@JvmStatic
val musicDirectory: File
get() {
val path = Settings.preferences
.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, defaultMusicDirectory.path)
val dir = File(path!!)
val path = Settings.cacheLocation
val dir = File(path)
val hasAccess = ensureDirectoryExistsAndIsReadWritable(dir)
if (!hasAccess) permissionUtil.value.handlePermissionFailed(null)
return if (hasAccess) dir else defaultMusicDirectory

View File

@ -0,0 +1,259 @@
package org.moire.ultrasonic.util
import android.Manifest
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Handler
import android.os.Looper
import androidx.core.content.PermissionChecker
import com.karumi.dexter.Dexter
import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import org.moire.ultrasonic.R
import org.moire.ultrasonic.util.FileUtil.defaultMusicDirectory
import timber.log.Timber
/**
* Contains static functions for Permission handling
*/
class PermissionUtil(private val applicationContext: Context) {
private var activityContext: Context? = null
interface PermissionRequestFinishedCallback {
fun onPermissionRequestFinished(hasPermission: Boolean)
}
fun onForegroundApplicationStarted(context: Context?) {
activityContext = context
}
fun onForegroundApplicationStopped() {
activityContext = null
}
/**
* This function can be used to handle file access permission failures.
*
* It will check if the failure is because the necessary permissions aren't available,
* and it will request them, if necessary.
*
* @param callback callback function to execute after the permission request is finished
*/
fun handlePermissionFailed(callback: PermissionRequestFinishedCallback?) {
val currentCachePath = Settings.cacheLocation
val defaultCachePath = defaultMusicDirectory.path
// Ultrasonic can do nothing about this error when the Music Directory is already set to the default.
if (currentCachePath.compareTo(defaultCachePath) == 0) return
if (PermissionChecker.checkSelfPermission(
applicationContext,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) == PermissionChecker.PERMISSION_DENIED ||
PermissionChecker.checkSelfPermission(
applicationContext,
Manifest.permission.READ_EXTERNAL_STORAGE
) == PermissionChecker.PERMISSION_DENIED
) {
// While we request permission, the Music Directory is temporarily reset to its default location
Settings.cacheLocation = defaultMusicDirectory.path
// If the application is not running, we can't notify the user
if (activityContext == null) return
requestFailedPermission(activityContext!!, currentCachePath, callback)
} else {
Settings.cacheLocation = defaultMusicDirectory.path
// If the application is not running, we can't notify the user
if (activityContext != null) {
Handler(Looper.getMainLooper()).post {
showWarning(
activityContext!!,
activityContext!!.getString(R.string.permissions_message_box_title),
activityContext!!.getString(R.string.permissions_access_error),
null
)
}
}
callback?.onPermissionRequestFinished(false)
}
}
companion object {
/**
* This function requests permission to access the filesystem.
* It can be used to request the permission initially, e.g. when the user decides to
* use a non-default folder for the cache
* @param context context for the operation
* @param callback callback function to execute after the permission request is finished
*/
@JvmStatic
fun requestInitialPermission(
context: Context,
callback: PermissionRequestFinishedCallback?
) {
Dexter.withContext(context)
.withPermissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
.withListener(object : MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport) {
if (report.areAllPermissionsGranted()) {
Timber.i("R/W permission granted for external storage")
callback?.onPermissionRequestFinished(true)
return
}
if (report.isAnyPermissionPermanentlyDenied) {
Timber.i(
"R/W permission is permanently denied for external storage"
)
showSettingsDialog(context)
callback?.onPermissionRequestFinished(false)
return
}
Timber.i("R/W permission is missing for external storage")
showWarning(
context,
context.getString(R.string.permissions_message_box_title),
context.getString(R.string.permissions_rationale_description_initial),
null
)
callback?.onPermissionRequestFinished(false)
}
override fun onPermissionRationaleShouldBeShown(
permissions: List<PermissionRequest>,
token: PermissionToken
) {
showWarning(
context,
context.getString(R.string.permissions_rationale_title),
context.getString(R.string.permissions_rationale_description_initial),
token
)
}
}).withErrorListener { error ->
Timber.e(
"An error has occurred during checking permissions with Dexter: %s",
error.toString()
)
}
.check()
}
private fun requestFailedPermission(
context: Context,
cacheLocation: String?,
callback: PermissionRequestFinishedCallback?
) {
Dexter.withContext(context)
.withPermissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
.withListener(object : MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport) {
if (report.areAllPermissionsGranted()) {
Timber.i("Permission granted to use cache directory %s", cacheLocation)
if (cacheLocation != null) {
Settings.cacheLocation = cacheLocation
}
callback?.onPermissionRequestFinished(true)
return
}
if (report.isAnyPermissionPermanentlyDenied) {
Timber.i(
"R/W permission for cache directory %s was permanently denied",
cacheLocation
)
showSettingsDialog(context)
callback?.onPermissionRequestFinished(false)
return
}
Timber.i(
"At least one permission is missing to use directory %s ",
cacheLocation
)
Settings.cacheLocation = defaultMusicDirectory.path
showWarning(
context, context.getString(R.string.permissions_message_box_title),
context.getString(R.string.permissions_permission_missing), null
)
callback?.onPermissionRequestFinished(false)
}
override fun onPermissionRationaleShouldBeShown(
permissions: List<PermissionRequest>,
token: PermissionToken
) {
showWarning(
context,
context.getString(R.string.permissions_rationale_title),
context.getString(R.string.permissions_rationale_description_failed),
token
)
}
}).withErrorListener { error ->
Timber.e(
"An error has occurred during checking permissions with Dexter: %s",
error.toString()
)
}
.check()
}
private fun showSettingsDialog(ctx: Context) {
val builder = Util.createDialog(
context = ctx,
android.R.drawable.ic_dialog_alert,
ctx.getString(R.string.permissions_permanent_denial_title),
ctx.getString(R.string.permissions_permanent_denial_description)
)
builder.setPositiveButton(ctx.getString(R.string.permissions_open_settings)) {
dialog, _ ->
dialog.cancel()
openSettings(ctx)
}
builder.setNegativeButton(ctx.getString(R.string.common_cancel)) { dialog, _ ->
Settings.cacheLocation = defaultMusicDirectory.path
dialog.cancel()
}
builder.show()
}
private fun openSettings(context: Context) {
val i = Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
i.addCategory(Intent.CATEGORY_DEFAULT)
i.data = Uri.parse("package:" + context.packageName)
context.startActivity(i)
}
private fun showWarning(
context: Context,
title: String,
text: String,
token: PermissionToken?
) {
val builder = Util.createDialog(
context = context,
android.R.drawable.ic_dialog_alert,
title,
text
)
builder.setPositiveButton(context.getString(R.string.common_ok)) { dialog, _ ->
dialog.cancel()
token?.continuePermissionRequest()
}
builder.show()
}
}
}

View File

@ -5,6 +5,8 @@ import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import org.moire.ultrasonic.R
private const val LUMINANCE_LIMIT = 0.25
object ServerColor {
fun getBackgroundColor(context: Context, serverColor: Int?): Int {
return serverColor ?: ContextCompat.getColor(
@ -17,10 +19,10 @@ object ServerColor {
context, Util.getResourceFromAttribute(context, R.attr.colorOnPrimary)
)
val luminance = ColorUtils.calculateLuminance(serverColor)
return if (luminance < 0.25) {
return if (luminance < LUMINANCE_LIMIT) {
ContextCompat.getColor(context, R.color.selected_menu_dark)
} else {
ContextCompat.getColor(context, R.color.selected_menu_light)
}
}
}
}

View File

@ -102,6 +102,23 @@ object Settings {
return if (preloadCount == -1) Int.MAX_VALUE else preloadCount
}
@JvmStatic
var cacheLocation: String
get() {
return preferences.getString(
Constants.PREFERENCES_KEY_CACHE_LOCATION,
FileUtil.defaultMusicDirectory.path
)!!
}
set(location) {
val editor = preferences.edit()
editor.putString(
Constants.PREFERENCES_KEY_CACHE_LOCATION,
location
)
editor.apply()
}
@JvmStatic
val cacheSizeMB: Int
get() {
@ -376,6 +393,21 @@ object Settings {
)
}
var shareOnServer: Boolean
get() {
val preferences = preferences
return preferences.getBoolean(Constants.PREFERENCES_KEY_SHARE_ON_SERVER, true)!!
}
set(shareOnServer) {
val preferences = preferences
val editor = preferences.edit()
editor.putBoolean(
Constants.PREFERENCES_KEY_SHARE_ON_SERVER,
shareOnServer
)
editor.apply()
}
var defaultShareExpiration: String
get() {
val preferences = preferences

View File

@ -80,6 +80,7 @@ object Util {
private const val EVENT_META_CHANGED = "org.moire.ultrasonic.EVENT_META_CHANGED"
private const val EVENT_PLAYSTATE_CHANGED = "org.moire.ultrasonic.EVENT_PLAYSTATE_CHANGED"
private const val CM_AVRCP_PLAYSTATE_CHANGED = "com.android.music.playstatechanged"
private const val CM_AVRCP_PLAYBACK_COMPLETE = "com.android.music.playbackcomplete"
private const val CM_AVRCP_METADATA_CHANGED = "com.android.music.metachanged"
// Used by hexEncode()
@ -392,17 +393,30 @@ object Util {
// The AlertDialog requires an Activity context, app context is not enough
// See https://stackoverflow.com/questions/5436822/
fun showDialog(context: Context?, icon: Int, titleId: Int, message: String?) {
AlertDialog.Builder(context)
fun createDialog(
context: Context?,
icon: Int = android.R.drawable.ic_dialog_info,
title: String,
message: String?
): AlertDialog.Builder {
return AlertDialog.Builder(context)
.setIcon(icon)
.setTitle(titleId)
.setTitle(title)
.setMessage(message)
.setPositiveButton(R.string.common_ok) {
dialog: DialogInterface,
_: Int ->
dialog.dismiss()
}
.show()
}
fun showDialog(
context: Context,
icon: Int = android.R.drawable.ic_dialog_info,
titleId: Int,
message: String?
) {
createDialog(context, icon, context.getString(titleId, ""), message).show()
}
@JvmStatic
@ -519,61 +533,14 @@ object Util {
listSize: Int,
id: Int
) {
if (!Settings.shouldSendBluetoothNotifications) {
return
}
if (!Settings.shouldSendBluetoothNotifications) return
var song: MusicDirectory.Entry? = null
val avrcpIntent = Intent(CM_AVRCP_METADATA_CHANGED)
if (currentPlaying != null) song = currentPlaying.song
if (song == null) {
avrcpIntent.putExtra("track", "")
avrcpIntent.putExtra("track_name", "")
avrcpIntent.putExtra("artist", "")
avrcpIntent.putExtra("artist_name", "")
avrcpIntent.putExtra("album", "")
avrcpIntent.putExtra("album_name", "")
avrcpIntent.putExtra("album_artist", "")
avrcpIntent.putExtra("album_artist_name", "")
fillIntent(avrcpIntent, song, playerPosition, id, listSize)
if (Settings.shouldSendBluetoothAlbumArt) {
avrcpIntent.putExtra("coverart", null as Parcelable?)
avrcpIntent.putExtra("cover", null as Parcelable?)
}
avrcpIntent.putExtra("ListSize", 0.toLong())
avrcpIntent.putExtra("id", 0.toLong())
avrcpIntent.putExtra("duration", 0.toLong())
avrcpIntent.putExtra("position", 0.toLong())
} else {
val title = song.title
val artist = song.artist
val album = song.album
val duration = song.duration
avrcpIntent.putExtra("track", title)
avrcpIntent.putExtra("track_name", title)
avrcpIntent.putExtra("artist", artist)
avrcpIntent.putExtra("artist_name", artist)
avrcpIntent.putExtra("album", album)
avrcpIntent.putExtra("album_name", album)
avrcpIntent.putExtra("album_artist", artist)
avrcpIntent.putExtra("album_artist_name", artist)
if (Settings.shouldSendBluetoothAlbumArt) {
val albumArtFile = FileUtil.getAlbumArtFile(song)
avrcpIntent.putExtra("coverart", albumArtFile.absolutePath)
avrcpIntent.putExtra("cover", albumArtFile.absolutePath)
}
avrcpIntent.putExtra("position", playerPosition.toLong())
avrcpIntent.putExtra("id", id.toLong())
avrcpIntent.putExtra("ListSize", listSize.toLong())
if (duration != null) {
avrcpIntent.putExtra("duration", duration.toLong())
}
}
context.sendBroadcast(avrcpIntent)
}
@ -586,54 +553,87 @@ object Util {
id: Int,
playerPosition: Int
) {
if (!Settings.shouldSendBluetoothNotifications) {
return
}
if (!Settings.shouldSendBluetoothNotifications) return
if (newSong != null) {
val avrcpIntent = Intent(CM_AVRCP_PLAYSTATE_CHANGED)
val title = newSong.title
val artist = newSong.artist
val album = newSong.album
val duration = newSong.duration
val avrcpIntent = Intent(
if (state == PlayerState.COMPLETED) CM_AVRCP_PLAYBACK_COMPLETE
else CM_AVRCP_PLAYSTATE_CHANGED
)
avrcpIntent.putExtra("track", title)
avrcpIntent.putExtra("track_name", title)
avrcpIntent.putExtra("artist", artist)
avrcpIntent.putExtra("artist_name", artist)
avrcpIntent.putExtra("album", album)
avrcpIntent.putExtra("album_name", album)
avrcpIntent.putExtra("album_artist", artist)
avrcpIntent.putExtra("album_artist_name", artist)
fillIntent(avrcpIntent, newSong, playerPosition, id, listSize)
if (Settings.shouldSendBluetoothAlbumArt) {
val albumArtFile = FileUtil.getAlbumArtFile(newSong)
avrcpIntent.putExtra("coverart", albumArtFile.absolutePath)
avrcpIntent.putExtra("cover", albumArtFile.absolutePath)
}
avrcpIntent.putExtra("position", playerPosition.toLong())
avrcpIntent.putExtra("id", id.toLong())
avrcpIntent.putExtra("ListSize", listSize.toLong())
if (duration != null) {
avrcpIntent.putExtra("duration", duration.toLong())
}
when (state) {
PlayerState.STARTED -> avrcpIntent.putExtra("playing", true)
PlayerState.STOPPED, PlayerState.PAUSED,
PlayerState.COMPLETED -> avrcpIntent.putExtra(
"playing",
false
)
else -> return // No need to broadcast.
if (state != PlayerState.COMPLETED) {
when (state) {
PlayerState.STARTED -> avrcpIntent.putExtra("playing", true)
PlayerState.STOPPED,
PlayerState.PAUSED -> avrcpIntent.putExtra("playing", false)
else -> return // No need to broadcast.
}
}
context.sendBroadcast(avrcpIntent)
}
}
private fun fillIntent(
intent: Intent,
song: MusicDirectory.Entry?,
playerPosition: Int,
id: Int,
listSize: Int
) {
if (song == null) {
intent.putExtra("track", "")
intent.putExtra("track_name", "")
intent.putExtra("artist", "")
intent.putExtra("artist_name", "")
intent.putExtra("album", "")
intent.putExtra("album_name", "")
intent.putExtra("album_artist", "")
intent.putExtra("album_artist_name", "")
if (Settings.shouldSendBluetoothAlbumArt) {
intent.putExtra("coverart", null as Parcelable?)
intent.putExtra("cover", null as Parcelable?)
}
intent.putExtra("ListSize", 0.toLong())
intent.putExtra("id", 0.toLong())
intent.putExtra("duration", 0.toLong())
intent.putExtra("position", 0.toLong())
} else {
val title = song.title
val artist = song.artist
val album = song.album
val duration = song.duration
intent.putExtra("track", title)
intent.putExtra("track_name", title)
intent.putExtra("artist", artist)
intent.putExtra("artist_name", artist)
intent.putExtra("album", album)
intent.putExtra("album_name", album)
intent.putExtra("album_artist", artist)
intent.putExtra("album_artist_name", artist)
if (Settings.shouldSendBluetoothAlbumArt) {
val albumArtFile = FileUtil.getAlbumArtFile(song)
intent.putExtra("coverart", albumArtFile.absolutePath)
intent.putExtra("cover", albumArtFile.absolutePath)
}
intent.putExtra("position", playerPosition.toLong())
intent.putExtra("id", id.toLong())
intent.putExtra("ListSize", listSize.toLong())
if (duration != null) {
intent.putExtra("duration", duration.toLong())
}
}
}
/**
*
* Broadcasts the given player state as the one being set.

View File

@ -9,8 +9,8 @@
android:bottomLeftRadius="44dp" />
<padding
android:paddingLeft="22dp"
android:paddingRight="22dp" />
android:paddingStart="22dp"
android:paddingEnd="22dp" />
<solid android:color="@color/selected_color_dark" />

View File

@ -110,8 +110,8 @@
a:layout_width="fill_parent"
a:layout_height="60dip"
a:layout_gravity="bottom|center_horizontal"
a:layout_marginLeft="60dip"
a:layout_marginRight="60dip"
a:layout_marginStart="60dip"
a:layout_marginEnd="60dip"
a:background="@color/translucent"
a:orientation="vertical"/>

View File

@ -15,7 +15,6 @@
a:layout_height="64dp"
a:layout_gravity="center_horizontal|center_vertical"
a:layout_marginStart="6dp"
a:layout_marginLeft="6dp"
a:layout_marginTop="6dp"
a:scaleType="fitCenter"
a:src="@drawable/unknown_album"
@ -28,22 +27,21 @@
a:layout_width="0dp"
a:layout_height="74dp"
a:layout_marginStart="10dp"
a:layout_marginLeft="10dp"
a:drawablePadding="6dip"
a:gravity="center_vertical"
a:minHeight="56dip"
a:orientation="vertical"
a:paddingLeft="3dip"
a:paddingRight="3dip"
a:paddingStart="3dip"
a:paddingEnd="3dip"
a:textAppearance="?android:attr/textAppearanceMedium"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintEnd_toStartOf="@+id/album_star"
app:layout_constraintLeft_toRightOf="@+id/album_coverart"
app:layout_constraintStart_toEndOf="@+id/album_coverart"
app:layout_constraintTop_toTopOf="parent">
<TextView
a:id="@+id/album_title"
a:layout_width="wrap_content"
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:ellipsize="marquee"
a:singleLine="true"
@ -52,7 +50,7 @@
<TextView
a:id="@+id/album_artist"
a:layout_width="wrap_content"
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"
@ -65,31 +63,15 @@
a:layout_width="38dp"
a:layout_height="38dp"
a:layout_marginStart="16dp"
a:layout_marginLeft="16dp"
a:layout_marginTop="16dp"
a:layout_marginEnd="20dp"
a:background="@android:color/transparent"
a:focusable="false"
a:gravity="center_horizontal"
a:paddingRight="3dip"
a:src="?attr/star_hollow"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toRightOf="@+id/row_album_details"
app:layout_constraintStart_toEndOf="@+id/row_album_details"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_star_hollow_dark"
a:paddingEnd="3dip" />
<androidx.constraintlayout.widget.Guideline
a:id="@+id/guideline"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:orientation="vertical"
app:layout_constraintGuide_begin="76dp" />
<androidx.constraintlayout.widget.Guideline
a:id="@+id/guideline2"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:orientation="vertical"
app:layout_constraintGuide_begin="346dp" />
tools:src="@drawable/ic_star_hollow_dark" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -9,17 +9,17 @@
a:id="@+id/album_coverart"
a:layout_width="64dp"
a:layout_height="64dp"
a:layout_gravity="left|center_vertical"
a:paddingLeft="3dip" />
a:layout_gravity="start|center_vertical"
a:paddingStart="3dip" />
<LinearLayout
a:orientation="vertical"
a:layout_width="0dip"
a:layout_height="wrap_content"
a:layout_weight="1"
a:layout_gravity="left|center_vertical"
a:paddingLeft="6dip"
a:paddingRight="3dip">
a:layout_gravity="start|center_vertical"
a:paddingStart="6dip"
a:paddingEnd="3dip">
<TextView
a:id="@+id/album_title"
@ -46,6 +46,6 @@
a:background="@android:color/transparent"
a:src="?attr/star_hollow"
a:focusable="false"
a:paddingRight="3dip" />
a:paddingEnd="3dip" />
</LinearLayout>

View File

@ -38,8 +38,8 @@
a:fadingEdge="horizontal"
a:fadingEdgeLength="20dip"
a:minHeight="16sp"
a:paddingLeft="4dip"
a:paddingRight="4dip"
a:paddingStart="4dip"
a:paddingEnd="4dip"
a:paddingTop="4dip"
a:singleLine="true"
a:gravity="center_horizontal"
@ -57,8 +57,8 @@
a:fadingEdgeLength="10dip"
a:minHeight="12sp"
a:paddingBottom="4dip"
a:paddingLeft="4dip"
a:paddingRight="4dip"
a:paddingStart="4dip"
a:paddingEnd="4dip"
a:singleLine="true"
a:gravity="center_horizontal"
a:text="Artist"

View File

@ -40,8 +40,8 @@
a:fadingEdge="horizontal"
a:fadingEdgeLength="20dip"
a:minHeight="16sp"
a:paddingLeft="4dip"
a:paddingRight="4dip"
a:paddingStart="4dip"
a:paddingEnd="4dip"
a:paddingTop="4dip"
a:singleLine="true"
a:gravity="center_horizontal"
@ -58,8 +58,8 @@
a:fadingEdge="horizontal"
a:fadingEdgeLength="10dip"
a:minHeight="12sp"
a:paddingLeft="4dip"
a:paddingRight="4dip"
a:paddingStart="4dip"
a:paddingEnd="4dip"
a:singleLine="true"
a:gravity="center_horizontal"
a:text="Artist"
@ -75,8 +75,8 @@
a:fadingEdgeLength="10dip"
a:minHeight="12sp"
a:paddingBottom="4dip"
a:paddingLeft="4dip"
a:paddingRight="4dip"
a:paddingStart="4dip"
a:paddingEnd="4dip"
a:singleLine="true"
a:gravity="center_horizontal"
a:text="Album"

View File

@ -42,8 +42,8 @@
a:fadingEdge="horizontal"
a:fadingEdgeLength="20dip"
a:minHeight="16sp"
a:paddingLeft="5dip"
a:paddingRight="5dip"
a:paddingStart="5dip"
a:paddingEnd="5dip"
a:singleLine="true"
a:textColor="@color/appwidget_text"
a:textSize="16sp"
@ -61,7 +61,7 @@
a:fadingEdgeLength="10dip"
a:minHeight="12sp"
a:paddingBottom="2dip"
a:paddingLeft="5dip"
a:paddingStart="5dip"
a:singleLine="true"
a:text="Artist"
a:layout_gravity="center_horizontal"

View File

@ -43,8 +43,8 @@
a:fadingEdge="horizontal"
a:fadingEdgeLength="20dip"
a:minHeight="16sp"
a:paddingLeft="5dip"
a:paddingRight="5dip"
a:paddingStart="5dip"
a:paddingEnd="5dip"
a:singleLine="true"
a:textColor="@color/appwidget_text"
a:textSize="16sp"
@ -62,7 +62,7 @@
a:fadingEdgeLength="10dip"
a:minHeight="12sp"
a:paddingBottom="2dip"
a:paddingLeft="5dip"
a:paddingStart="5dip"
a:singleLine="true"
a:text="Artist"
a:layout_gravity="center_horizontal"

View File

@ -16,9 +16,7 @@
a:minWidth="56dip"
a:minHeight="56dip"
a:paddingStart="8dip"
a:paddingLeft="8dip"
a:paddingEnd="8dip"
a:paddingRight="8dip"
a:text="A"
a:textAppearance="?android:attr/textAppearanceLarge"
a:textColor="@color/cyan" />
@ -30,11 +28,8 @@
a:layout_gravity="center_horizontal|center_vertical"
a:layout_marginTop="8dp"
a:layout_marginStart="2dp"
a:layout_marginLeft="2dp"
a:layout_marginEnd="10dp"
a:layout_marginRight="10dp"
a:layout_toEndOf="@+id/row_section"
a:layout_toRightOf="@+id/row_section"
a:scaleType="fitCenter"
a:src="@drawable/ic_contact_picture"
app:shapeAppearanceOverlay="@style/roundedImageView" />
@ -44,13 +39,12 @@
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:layout_toEndOf="@+id/artist_coverart"
a:layout_toRightOf="@+id/artist_coverart"
a:drawablePadding="6dip"
a:gravity="center_vertical"
a:minHeight="56dip"
a:paddingLeft="3dip"
a:paddingRight="3dip"
a:layout_marginRight="12dp"
a:paddingStart="3dip"
a:paddingEnd="3dip"
a:layout_marginEnd="12dp"
a:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>

View File

@ -20,8 +20,8 @@
a:id="@+id/chat_username"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginLeft="6dip"
a:layout_marginRight="6dip"
a:layout_marginStart="6dip"
a:layout_marginEnd="6dip"
a:ellipsize="marquee"
a:singleLine="true"
a:textIsSelectable="true"
@ -43,7 +43,7 @@
a:id="@+id/chat_time"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginLeft="6dip"
a:layout_marginStart="6dip"
a:singleLine="true"
a:textIsSelectable="true"
a:textAppearance="?android:attr/textAppearanceMedium"
@ -53,8 +53,8 @@
a:id="@+id/chat_message"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginLeft="6dip"
a:layout_marginRight="6dip"
a:layout_marginStart="6dip"
a:layout_marginEnd="6dip"
a:textIsSelectable="true"
a:linksClickable="true"
a:singleLine="false"

View File

@ -17,7 +17,7 @@
a:id="@+id/chat_username"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginRight="6dip"
a:layout_marginEnd="6dip"
a:gravity="center_vertical|right"
a:layout_gravity="right"
a:ellipsize="marquee"
@ -38,7 +38,7 @@
a:id="@+id/chat_time"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginLeft="6dip"
a:layout_marginStart="6dip"
a:singleLine="true"
a:gravity="center_vertical|right"
a:textIsSelectable="true"
@ -49,8 +49,8 @@
a:id="@+id/chat_message"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginLeft="6dip"
a:layout_marginRight="6dip"
a:layout_marginStart="6dip"
a:layout_marginEnd="6dip"
a:linksClickable="true"
a:singleLine="false"
a:autoLink="all"

View File

@ -110,8 +110,8 @@
a:layout_height="60dip"
a:layout_gravity="center"
a:background="@color/translucent"
a:layout_marginLeft="80dip"
a:layout_marginRight="80dip"
a:layout_marginStart="80dip"
a:layout_marginEnd="80dip"
a:orientation="vertical"
/>

View File

@ -36,8 +36,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dip"
android:paddingLeft="40dip"
android:paddingRight="40dip"/>
android:paddingStart="40dip"
android:paddingEnd="40dip"/>
</LinearLayout>
</ScrollView>

View File

@ -24,7 +24,7 @@
a:layout_height="wrap_content"
a:layout_marginTop="8dp"
a:layout_alignParentRight="true"
a:layout_toRightOf="@+id/equalizer.frequency"
a:layout_toEndOf="@+id/equalizer.frequency"
/>
<SeekBar

View File

@ -29,10 +29,8 @@
a:layout_height="wrap_content"
a:layout_alignParentBottom="true"
a:layout_marginStart="10dp"
a:layout_marginLeft="10dp"
a:layout_marginTop="10dp"
a:layout_marginEnd="10dp"
a:layout_marginRight="10dp"
a:layout_marginBottom="10dp"
a:drawableStart="?attr/filepicker_create_new_folder"
a:drawableLeft="?attr/filepicker_create_new_folder"

View File

@ -19,7 +19,6 @@
a:layout_height="wrap_content"
a:layout_gravity="center_vertical"
a:layout_marginStart="20dp"
a:layout_marginLeft="20dp"
a:gravity="center_vertical"
a:text=""
a:textSize="18sp" />

View File

@ -6,6 +6,6 @@
a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceMedium"
a:gravity="center_vertical"
a:paddingLeft="3dip"
a:paddingRight="3dip"
a:paddingStart="3dip"
a:paddingEnd="3dip"
a:minHeight="50dip"/>

View File

@ -14,8 +14,8 @@
a:textColor="#ffffffff"
a:shadowColor="#bb000000"
a:shadowRadius="2.75"
a:paddingLeft="32dp"
a:paddingRight="32dp"
a:paddingStart="32dp"
a:paddingEnd="32dp"
a:paddingBottom="12dp"
/>

View File

@ -28,8 +28,8 @@
a:gravity="center_horizontal"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:paddingLeft="10dip"
a:paddingRight="10dip"
a:paddingStart="10dip"
a:paddingEnd="10dip"
a:paddingTop="10dip"
a:paddingBottom="4dip"
/>
@ -40,8 +40,8 @@
a:gravity="center_horizontal"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:paddingLeft="10dip"
a:paddingRight="10dip"
a:paddingStart="10dip"
a:paddingEnd="10dip"
/>
<TextView
@ -51,8 +51,8 @@
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:paddingTop="32dip"
a:paddingLeft="10dip"
a:paddingRight="10dip"
a:paddingStart="10dip"
a:paddingEnd="10dip"
/>
</LinearLayout>

View File

@ -5,95 +5,95 @@
<TextView a:id="@+id/main_music" a:text="@string/main.music"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceSmall" a:textColor="@color/cyan"
a:gravity="center_vertical" a:paddingLeft="6dp" a:textAllCaps="true"
a:gravity="center_vertical" a:paddingStart="6dp" a:textAllCaps="true"
a:textStyle="bold" />
<TextView a:id="@+id/main_artists_button" a:text="@string/main.artists_title"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceMedium" a:gravity="center_vertical"
a:paddingLeft="6dip" a:paddingRight="6dip" a:minHeight="40dip" />
a:paddingStart="6dip" a:paddingEnd="6dip" a:minHeight="40dip" />
<TextView a:id="@+id/main_albums_button" a:text="@string/main.albums_title"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceMedium" a:gravity="center_vertical"
a:paddingLeft="6dip" a:paddingRight="6dip" a:minHeight="40dip" />
a:paddingStart="6dip" a:paddingEnd="6dip" a:minHeight="40dip" />
<TextView a:id="@+id/main_genres_button" a:text="@string/main.genres_title"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceMedium" a:gravity="center_vertical"
a:paddingLeft="6dip" a:paddingRight="6dip" a:minHeight="40dip" />
a:paddingStart="6dip" a:paddingEnd="6dip" a:minHeight="40dip" />
<TextView a:id="@+id/main_songs" a:text="@string/main.songs_title"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceSmall" a:textColor="@color/cyan"
a:gravity="center_vertical" a:paddingLeft="6dp" a:textAllCaps="true"
a:gravity="center_vertical" a:paddingStart="6dp" a:textAllCaps="true"
a:textStyle="bold" />
<TextView a:id="@+id/main_songs_button" a:text="@string/main.songs_random"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceMedium" a:gravity="center_vertical"
a:paddingLeft="6dip" a:paddingRight="6dip" a:minHeight="40dip" />
a:paddingStart="6dip" a:paddingEnd="6dip" a:minHeight="40dip" />
<TextView a:id="@+id/main_songs_starred" a:text="@string/main.songs_starred"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceMedium" a:gravity="center_vertical"
a:paddingLeft="6dip" a:paddingRight="6dip" a:minHeight="40dip" />
a:paddingStart="6dip" a:paddingEnd="6dip" a:minHeight="40dip" />
<TextView a:id="@+id/main_albums" a:text="@string/main.albums_title"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceSmall" a:textColor="@color/cyan"
a:gravity="center_vertical" a:paddingLeft="6dp" a:textAllCaps="true"
a:gravity="center_vertical" a:paddingStart="6dp" a:textAllCaps="true"
a:textStyle="bold" />
<TextView a:id="@+id/main_albums_newest" a:text="@string/main.albums_newest"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceMedium" a:gravity="center_vertical"
a:paddingLeft="6dip" a:paddingRight="6dip" a:minHeight="40dip" />
a:paddingStart="6dip" a:paddingEnd="6dip" a:minHeight="40dip" />
<TextView a:id="@+id/main_albums_recent" a:text="@string/main.albums_recent"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceMedium" a:gravity="center_vertical"
a:paddingLeft="6dip" a:paddingRight="6dip" a:minHeight="40dip" />
a:paddingStart="6dip" a:paddingEnd="6dip" a:minHeight="40dip" />
<TextView a:id="@+id/main_albums_frequent" a:text="@string/main.albums_frequent"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceMedium" a:gravity="center_vertical"
a:paddingLeft="6dip" a:paddingRight="6dip" a:minHeight="40dip" />
a:paddingStart="6dip" a:paddingEnd="6dip" a:minHeight="40dip" />
<TextView a:id="@+id/main_albums_highest" a:text="@string/main.albums_highest"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceMedium" a:gravity="center_vertical"
a:paddingLeft="6dip" a:paddingRight="6dip" a:minHeight="40dip" />
a:paddingStart="6dip" a:paddingEnd="6dip" a:minHeight="40dip" />
<TextView a:id="@+id/main_albums_random" a:text="@string/main.albums_random"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceMedium" a:gravity="center_vertical"
a:paddingLeft="6dip" a:paddingRight="6dip" a:minHeight="40dip" />
a:paddingStart="6dip" a:paddingEnd="6dip" a:minHeight="40dip" />
<TextView a:id="@+id/main_albums_starred" a:layout_width="fill_parent"
a:layout_height="wrap_content" a:gravity="center_vertical"
a:minHeight="40dip" a:paddingLeft="6dip" a:paddingRight="6dip"
a:minHeight="40dip" a:paddingStart="6dip" a:paddingEnd="6dip"
a:text="@string/main.albums_starred" a:textAppearance="?android:attr/textAppearanceMedium" />
<TextView a:id="@+id/main_albums_alphaByName" a:layout_width="fill_parent"
a:layout_height="wrap_content" a:gravity="center_vertical"
a:minHeight="40dip" a:paddingLeft="6dip" a:paddingRight="6dip"
a:minHeight="40dip" a:paddingStart="6dip" a:paddingEnd="6dip"
a:text="@string/main.albums_alphaByName" a:textAppearance="?android:attr/textAppearanceMedium" />
<TextView a:id="@+id/main_albums_alphaByArtist" a:layout_width="fill_parent"
a:layout_height="wrap_content" a:gravity="center_vertical"
a:minHeight="40dip" a:paddingLeft="6dip" a:paddingRight="6dip"
a:minHeight="40dip" a:paddingStart="6dip" a:paddingEnd="6dip"
a:text="@string/main.albums_alphaByArtist" a:textAppearance="?android:attr/textAppearanceMedium" />
<TextView a:id="@+id/main_videos_title" a:text="@string/main.videos"
a:layout_width="fill_parent" a:layout_height="wrap_content"
a:textAppearance="?android:attr/textAppearanceSmall" a:textColor="@color/cyan"
a:gravity="center_vertical" a:paddingLeft="6dp" a:textAllCaps="true"
a:gravity="center_vertical" a:paddingStart="6dp" a:textAllCaps="true"
a:textStyle="bold" />
<TextView a:id="@+id/main_videos" a:layout_width="fill_parent"
a:layout_height="wrap_content" a:gravity="center_vertical"
a:minHeight="40dip" a:paddingLeft="6dip" a:paddingRight="6dip"
a:minHeight="40dip" a:paddingStart="6dip" a:paddingEnd="6dip"
a:text="@string/main.videos" a:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>

View File

@ -4,8 +4,8 @@
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:orientation="horizontal"
a:layout_marginLeft="12dp"
a:layout_marginRight="12dp" >
a:layout_marginStart="12dp"
a:layout_marginEnd="12dp" >
<ImageView
a:id="@+id/button_shuffle"

View File

@ -56,8 +56,8 @@
app:iconTint="?attr/colorOnPrimary"
a:paddingTop="16dp"
a:paddingBottom="16dp"
a:paddingLeft="22dp"
a:paddingRight="22dp"
a:paddingStart="22dp"
a:paddingEnd="22dp"
a:text="@string/main.offline"
a:textColor="?attr/colorOnPrimary"
a:background="@drawable/default_ripple"

View File

@ -19,7 +19,6 @@
android:id="@+id/now_playing_image"
android:layout_width="64.0dip"
android:layout_height="64.0dip"
android:layout_marginLeft="6dp"
android:focusable="true"
android:gravity="center"
android:layout_marginStart="6dp" />
@ -30,14 +29,13 @@
android:layout_gravity="center_vertical"
android:layout_weight="1.0"
android:orientation="vertical"
android:paddingLeft="11.0dip"
android:paddingStart="11.0dip">
<TextView
android:id="@+id/now_playing_trackname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_gravity="start"
android:ellipsize="marquee"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
@ -47,7 +45,7 @@
android:id="@+id/now_playing_artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_gravity="start"
android:ellipsize="end"
android:scrollHorizontally="true"
android:singleLine="true"
@ -58,9 +56,9 @@
android:id="@+id/now_playing_control_play"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center|right"
android:layout_gravity="center|end"
android:layout_marginTop="2dip"
android:layout_marginRight="16dip"
android:layout_marginEnd="16dip"
android:layout_weight="0.0"
android:focusable="false"
android:scaleType="fitCenter"

View File

@ -5,8 +5,8 @@
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:layout_marginTop="10dp"
a:layout_marginLeft="12dp"
a:layout_marginRight="12dp" >
a:layout_marginStart="12dp"
a:layout_marginEnd="12dp" >
<LinearLayout
a:layout_width="wrap_content"

View File

@ -4,8 +4,8 @@
a:layout_height="wrap_content"
a:orientation="vertical"
a:layout_marginTop="10dp"
a:layout_marginLeft="12dp"
a:layout_marginRight="12dp" >
a:layout_marginStart="12dp"
a:layout_marginEnd="12dp" >
<SeekBar
a:id="@+id/current_playing_progress_bar"
@ -22,7 +22,7 @@
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_alignParentLeft="true"
a:layout_marginLeft="12dip"
a:layout_marginStart="12dip"
a:text="@string/util.no_time"
a:textAppearance="?android:attr/textAppearanceSmall" />
@ -31,7 +31,7 @@
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_alignParentRight="true"
a:layout_marginRight="12dip"
a:layout_marginEnd="12dip"
a:text="@string/util.no_time"
a:textAppearance="?android:attr/textAppearanceSmall" />

View File

@ -11,8 +11,8 @@
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceMedium"
android:gravity="left|center_vertical"
android:paddingLeft="6dip"
android:paddingRight="6dip"
android:paddingStart="6dip"
android:paddingEnd="6dip"
android:minHeight="50dip"/>
</LinearLayout>

View File

@ -24,7 +24,7 @@
a:textStyle="bold"
a:background="#ff555555"
a:gravity="center_vertical"
a:paddingLeft="4dp"/>
a:paddingStart="4dp"/>
<TextView
a:id="@+id/search_albums"
@ -36,7 +36,7 @@
a:textStyle="bold"
a:background="#ff555555"
a:gravity="center_vertical"
a:paddingLeft="4dp"/>
a:paddingStart="4dp"/>
<TextView
a:id="@+id/search_songs"
@ -48,7 +48,7 @@
a:textStyle="bold"
a:background="#ff555555"
a:gravity="center_vertical"
a:paddingLeft="4dp"/>
a:paddingStart="4dp"/>
<TextView
a:id="@+id/search_more_artists"

View File

@ -9,7 +9,7 @@
a:layout_height="160dip"
a:layout_alignParentLeft="true"
a:layout_alignParentTop="true"
a:layout_marginRight="10dip"
a:layout_marginEnd="10dip"
a:contentDescription="@null"
a:scaleType="fitCenter"
a:src="@drawable/unknown_album"/>
@ -18,9 +18,9 @@
a:id="@+id/select_album_title"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_toRightOf="@+id/select_album_art"
a:layout_toEndOf="@+id/select_album_art"
a:ellipsize="end"
a:paddingRight="4dip"
a:paddingEnd="4dip"
a:paddingTop="10dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceMedium"/>
@ -30,9 +30,9 @@
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_below="@+id/select_album_title"
a:layout_toRightOf="@+id/select_album_art"
a:layout_toEndOf="@+id/select_album_art"
a:ellipsize="end"
a:paddingRight="4dip"
a:paddingEnd="4dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/>
@ -41,9 +41,9 @@
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_below="@+id/select_album_artist"
a:layout_toRightOf="@+id/select_album_art"
a:layout_toEndOf="@+id/select_album_art"
a:ellipsize="end"
a:paddingRight="4dip"
a:paddingEnd="4dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/>
@ -52,9 +52,9 @@
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_below="@+id/select_album_genre"
a:layout_toRightOf="@+id/select_album_art"
a:layout_toEndOf="@+id/select_album_art"
a:ellipsize="end"
a:paddingRight="4dip"
a:paddingEnd="4dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/>
@ -63,9 +63,9 @@
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_below="@+id/select_album_year"
a:layout_toRightOf="@+id/select_album_art"
a:layout_toEndOf="@+id/select_album_art"
a:ellipsize="none"
a:paddingRight="4dip"
a:paddingEnd="4dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/>
@ -74,9 +74,9 @@
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_below="@+id/select_album_song_count"
a:layout_toRightOf="@+id/select_album_art"
a:layout_toEndOf="@+id/select_album_art"
a:ellipsize="none"
a:paddingRight="4dip"
a:paddingEnd="4dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/>

View File

@ -6,7 +6,7 @@
a:minHeight="?android:attr/listPreferredItemHeight"
a:orientation="horizontal"
a:paddingBottom="2dip"
a:paddingLeft="6dp"
a:paddingStart="6dp"
a:paddingTop="2dip"
a:background="?android:attr/selectableItemBackground"
a:clickable="true"
@ -27,7 +27,7 @@
a:id="@+id/select_folder_title"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:layout_marginLeft="10dip"
a:layout_marginStart="10dip"
a:layout_marginTop="6dip"
a:text="@string/select_artist.folder"
a:textAppearance="?android:attr/textAppearanceLarge" />
@ -36,7 +36,7 @@
a:id="@+id/select_folder_name"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:layout_marginLeft="10dip"
a:layout_marginStart="10dip"
a:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>

View File

@ -131,7 +131,6 @@
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:layout_marginLeft="5dp"
a:text="@string/settings.title.allow_self_signed_certificate"
app:layout_constraintBottom_toTopOf="@id/edit_ldap_title"
app:layout_constraintStart_toStartOf="parent"
@ -142,9 +141,7 @@
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="8dp"
a:layout_marginLeft="8dp"
a:layout_marginEnd="5dp"
a:layout_marginRight="5dp"
a:layout_marginBottom="8dp"
a:checked="false"
app:layout_constraintBottom_toTopOf="@id/edit_ldap_title"
@ -158,7 +155,6 @@
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:layout_marginLeft="5dp"
a:text="@string/settings.title.enable_ldap_users_support"
app:layout_constraintBottom_toTopOf="@id/edit_ldap_description"
app:layout_constraintStart_toStartOf="parent"
@ -170,7 +166,6 @@
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:layout_marginLeft="5dp"
a:text="@string/settings.summary.enable_ldap_users_support"
app:layout_constraintBottom_toTopOf="@id/edit_jukebox"
app:layout_constraintEnd_toStartOf="@id/edit_ldap"
@ -182,8 +177,6 @@
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginEnd="5dp"
a:layout_marginRight="5dp"
a:layout_marginLeft="8dp"
a:layout_marginStart="8dp"
a:checked="false"
app:layout_constraintBottom_toBottomOf="@id/edit_ldap_description"
@ -197,7 +190,6 @@
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="5dp"
a:layout_marginLeft="5dp"
a:layout_marginTop="8dp"
a:text="@string/jukebox.is_default"
app:layout_constraintStart_toStartOf="parent"
@ -208,10 +200,8 @@
a:layout_width="0dp"
a:layout_height="wrap_content"
a:layout_marginStart="8dp"
a:layout_marginLeft="8dp"
a:layout_marginTop="8dp"
a:layout_marginEnd="5dp"
a:layout_marginRight="5dp"
a:checked="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/edit_jukebox_title"

View File

@ -55,7 +55,7 @@
a:layout_centerVertical="true"
a:layout_gravity="end"
a:layout_marginEnd="15dp"
a:layout_marginRight="15dp"
a:focusable="false"
a:src="?attr/more_vert"
tools:src="@drawable/ic_more_vert_dark"

View File

@ -10,6 +10,14 @@
a:layout_width="wrap_content"
a:layout_height="wrap_content">
<CheckBox
a:id="@+id/share_on_server"
a:text="@string/share_on_server"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginTop="4dip"
a:layout_marginBottom="4dip" />
<TextView
a:layout_width="wrap_content"
a:layout_height="wrap_content"

View File

@ -4,8 +4,8 @@
a:layout_height="wrap_content"
a:layout_gravity="center_vertical"
a:layout_weight="1"
a:paddingLeft="6dip"
a:paddingRight="6dip"
a:paddingStart="6dip"
a:paddingEnd="6dip"
a:minHeight="44dip"
a:orientation="vertical">
@ -38,7 +38,7 @@
a:layout_gravity="left|center_vertical"
a:layout_weight="1"
a:ellipsize="middle"
a:paddingLeft="4dip"
a:paddingStart="4dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/>

View File

@ -4,7 +4,7 @@
a:layout_height="wrap_content"
a:layout_gravity="center_vertical"
a:layout_weight="1"
a:layout_marginLeft="4dp"
a:layout_marginStart="4dp"
a:orientation="vertical">
<LinearLayout
@ -28,8 +28,8 @@
a:layout_weight="1"
a:drawablePadding="6dip"
a:ellipsize="end"
a:paddingLeft="4dip"
a:paddingRight="2dip"
a:paddingStart="4dip"
a:paddingEnd="2dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceMedium"/>
@ -39,7 +39,7 @@
a:layout_height="wrap_content"
a:layout_gravity="right|center_vertical"
a:drawablePadding="6dip"
a:paddingRight="6dip"/>
a:paddingEnd="6dip"/>
</LinearLayout>
<LinearLayout
@ -55,7 +55,7 @@
a:layout_gravity="left|center_vertical"
a:layout_weight="1"
a:ellipsize="middle"
a:paddingLeft="4dip"
a:paddingStart="4dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/>
@ -64,8 +64,8 @@
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_gravity="right|center_vertical"
a:paddingLeft="3dip"
a:paddingRight="9dip"
a:paddingStart="3dip"
a:paddingEnd="9dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/>
</LinearLayout>

View File

@ -10,9 +10,7 @@
a:id="@+id/song_drag"
a:layout_width="wrap_content"
a:layout_height="fill_parent"
a:paddingLeft="5dip"
a:paddingStart="5dip"
a:paddingRight="0dip"
a:paddingEnd="0dip"
a:background="@android:color/transparent"
a:focusable="false"
@ -25,7 +23,7 @@
a:layout_height="fill_parent"
a:checkMark="?attr/button_check_custom"
a:gravity="center_vertical"
a:paddingRight="4dip"/>
a:paddingEnd="4dip"/>
<include layout="@layout/song_details" />
@ -81,7 +79,7 @@
a:id="@+id/song_five_star_5"
a:layout_width="10dip"
a:layout_height="fill_parent"
a:layout_marginRight="8dip"
a:layout_marginEnd="8dip"
a:background="@android:color/transparent"
a:focusable="false"
a:gravity="center_vertical"
@ -97,7 +95,7 @@
a:background="@android:color/transparent"
a:focusable="false"
a:gravity="center_vertical"
a:paddingRight="8dip"
a:paddingEnd="8dip"
a:src="?attr/star_hollow" />
</LinearLayout>

View File

@ -44,7 +44,7 @@
android:layout_height="wrap_content"
android:id="@+id/timeSpanSpinner"
android:layout_alignBottom="@+id/timeSpanEditText"
android:layout_toRightOf="@+id/timeSpanEditText"/>
android:layout_toEndOf="@+id/timeSpanEditText"/>
</RelativeLayout>
</LinearLayout>

View File

@ -12,7 +12,7 @@
android:id="@+id/get_playlist_name_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:textSize="20sp"
android:text="@string/common.name" />
<EditText
@ -21,7 +21,7 @@
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:hint="@string/common.name" />
</LinearLayout>
@ -34,7 +34,7 @@
android:id="@+id/get_playlist_comment_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:textSize="20sp"
android:text="@string/common.comment" />
<EditText
@ -43,7 +43,7 @@
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:hint="@string/common.comment" />
</LinearLayout>
@ -56,7 +56,7 @@
android:id="@+id/get_playlist_public_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:textSize="20sp"
android:text="@string/common.public" />
<CheckBox
@ -64,7 +64,7 @@
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:checked="false"/>
</LinearLayout>
</LinearLayout>

View File

@ -19,8 +19,8 @@
a:layout_gravity="left|center_vertical"
a:layout_weight="1"
a:ellipsize="end"
a:paddingLeft="4dip"
a:paddingRight="2dip"
a:paddingStart="4dip"
a:paddingEnd="2dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceMedium"/>
@ -29,8 +29,8 @@
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_gravity="right|center_vertical"
a:paddingLeft="2dip"
a:paddingRight="6dip"
a:paddingStart="2dip"
a:paddingEnd="6dip"
a:singleLine="true"
a:textAppearance="?android:attr/textAppearanceSmall"/>
</LinearLayout>

View File

@ -10,7 +10,7 @@
a:layout_width="wrap_content"
a:layout_height="fill_parent"
a:gravity="center_vertical"
a:paddingLeft="1dip"
a:paddingStart="1dip"
a:visibility="gone" />
<include layout="@layout/video_details"
@ -23,7 +23,7 @@
a:background="@android:color/transparent"
a:focusable="false"
a:gravity="center_vertical"
a:paddingRight="8dip"
a:paddingEnd="8dip"
a:src="?attr/star_hollow" />
</LinearLayout>

View File

@ -14,6 +14,11 @@
a:icon="?attr/star_hollow"
app:showAsAction="ifRoom|withText"
a:title="@string/download.menu_star"/>
<item
a:id="@+id/menu_item_share_song"
a:icon="?attr/share"
app:showAsAction="ifRoom|withText"
a:title="@string/download.share_song"/>
<item
a:id="@+id/menu_item_share"
a:icon="?attr/share"

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="background_task.loading">Načítám&#8230;</string>
<string name="background_task.network_error">Chyba sítě. Ověřte adresu serveru nebo zkuste později.</string>
@ -17,7 +17,7 @@
<string name="button_bar.now_playing">Právě hraje</string>
<string name="podcasts.label">Podcasty</string>
<string name="podcasts_channels.empty">Není registrován žádný podcast kanál</string>
<string name="button_bar.podcasts">Podcast</string>
<string name="button_bar.podcasts">Podcasty</string>
<string name="button_bar.playlists">Playlisty</string>
<string name="button_bar.search">Hledat</string>
<string name="chat.send_a_message">Poslat zprávu</string>
@ -301,7 +301,9 @@
<string name="settings.use_folder_for_album_artist_summary">Očekává jména hlavních adresářů obsahující jména umělců</string>
<string name="settings.use_id3">Procházet za použití ID3 tagů</string>
<string name="settings.use_id3_summary">Používat metodu ID3 tagů místo jmen na základě adresářové struktury</string>
<string name="main.video">Video</string>
<string name="settings.show_artist_picture">Obrázek umělce v seznamu umělců</string>
<string name="settings.show_artist_picture_summary">Zobrazí obrázek umělce v náhledu umělců pokud je dostupný</string>
<string name="main.video" tools:ignore="UnusedResources">Video</string>
<string name="settings.view_refresh">Obnovení náhledu</string>
<string name="settings.view_refresh_500">.5 sekundy</string>
<string name="settings.view_refresh_1000">1 sekunda</string>
@ -321,14 +323,13 @@
<string name="util.bytes_format.gigabyte">0.00 GB</string>
<string name="util.bytes_format.kilobyte">0 KB</string>
<string name="util.bytes_format.megabyte">0.00 MB</string>
<string name="util.no_time">-:--</string>
<string name="util.no_time" tools:ignore="TypographyDashes">-:--</string>
<string name="util.zero_time">0:00</string>
<string name="widget.initial_text">Ťuknutím vybrat hudbu</string>
<string name="widget.sdcard_busy">SD karta nedostupná</string>
<string name="widget.sdcard_missing">Chybí SD karta</string>
<string name="settings.share_description_default">Výchozí popis sdílení</string>
<string name="settings.sharing_title">Sdílení</string>
<string name="settings.sharing_always_ask_for_details_summary">Vždy se dotazovat na popis a čas vypršení při vytváření sdílení</string>
<string name="settings.sharing_always_ask_for_details">Vždy se dotazovat na detaily</string>
<string name="settings.share_expiration_default">Výchozí čas vypršení</string>
<string name="do_not_show_dialog_again">Dialog opět nezobrazovat</string>
@ -379,7 +380,6 @@
<string name="settings.debug.log_keep">Zachovat soubory</string>
<string name="settings.debug.log_delete">Smazat soubory</string>
<string name="settings.debug.log_deleted">Smazat soubory logů.</string>
<string name="permissions.access_error">Ultrasonic nemá přístup k odkládacím souborům hudby. Umístění odkládacího adresáře bylo změněno na výchozí hodnotu.</string>
<string name="permissions.message_box_title">Varování</string>
<string name="permissions.permission_missing">Ultrasonic vyžaduje práva čtení/zápisu do hudebního odkládacího adresáře. Umístění odkládacího adresáře bylo změněno na výchozí hodnotu.</string>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="background_task.loading">Lade&#8230;</string>
<string name="background_task.network_error">Ein Netzwerkfehler ist aufgetreten. Bitte die Serveradresse prüfen oder später noch einmal versuchen.</string>
@ -298,7 +298,7 @@
<string name="settings.use_folder_for_album_artist_summary">Annehmen, dass der Ordner der obersten Ebene der Name des Albumkünstlers ist</string>
<string name="settings.use_id3">Durchsuchen von ID3-Tags</string>
<string name="settings.use_id3_summary">Nutze ID3 Tag Methode anstatt Dateisystem-Methode</string>
<string name="main.video">Film</string>
<string name="main.video" tools:ignore="UnusedResources">Film</string>
<string name="settings.view_refresh">Aktualisierungsinterval</string>
<string name="settings.view_refresh_500">.5 Sekunden</string>
<string name="settings.view_refresh_1000">1 Sekunde</string>
@ -318,14 +318,13 @@
<string name="util.bytes_format.gigabyte">0.00 GB</string>
<string name="util.bytes_format.kilobyte">0 KB</string>
<string name="util.bytes_format.megabyte">0.00 MB</string>
<string name="util.no_time">-:--</string>
<string name="util.no_time" tools:ignore="TypographyDashes">-:--</string>
<string name="util.zero_time">0:00</string>
<string name="widget.initial_text">Berühren, um Musik auszuwählen</string>
<string name="widget.sdcard_busy">SD Karte nicht verfügbar</string>
<string name="widget.sdcard_missing">Keine SD Karte</string>
<string name="settings.share_description_default">Standard Beschreibung einer Freigabe</string>
<string name="settings.sharing_title">Teilen</string>
<string name="settings.sharing_always_ask_for_details_summary">Beim erstellen einer Freigabe immer nach Beschreibung und Ablaufdatum fragen</string>
<string name="settings.sharing_always_ask_for_details">Immer nach Details fragen</string>
<string name="settings.share_expiration_default">Standard Ablaufzeit</string>
<string name="do_not_show_dialog_again">Dialog nicht wieder anzeigen</string>
@ -362,35 +361,6 @@
<string name="download.menu_show_artist">Künstler zeigen</string>
<string name="common_multiple_years">Mehrere Jahre</string>
<string name="server_editor.new_label">Server hinzufügen</string>
<plurals name="select_album_n_songs">
<item quantity="one">%d Titel</item>
<item quantity="other">%d Titel</item>
</plurals>
<plurals name="select_album_n_songs_pinned">
<item quantity="one">%d Titel</item>
<item quantity="other">%d Titel</item>
</plurals>
<plurals name="select_album_n_songs_downloaded">
<item quantity="one">%d Titel</item>
<item quantity="other">%d Titel</item>
</plurals>
<plurals name="select_album_n_songs_unpinned">
<item quantity="one">%d Titel</item>
<item quantity="other">%d Titel</item>
</plurals>
<plurals name="select_album_n_songs_added">
<item quantity="one">%d Titel</item>
<item quantity="other">%d Titel</item>
</plurals>
<plurals name="select_album_n_songs_play_next">
<item quantity="one">%d Titel</item>
<item quantity="other">%d Titel</item>
</plurals>
<plurals name="select_album_donate_dialog_n_trial_days_left">
<item quantity="one">%d Titel</item>
<item quantity="other">%d Titel</item>
</plurals>
<!-- Subsonic api errors -->
<string name="api.subsonic.generic">Allgemeiner API Fehler: %1$s</string>
<string name="api.subsonic.generic.no.message">Keine Nachricht vom Server erhalten</string>
@ -401,7 +371,7 @@
<string name="api.subsonic.requested_data_was_not_found">Angeforderte Daten nicht gefunden.</string>
<string name="api.subsonic.trial_period_is_over">Der Testzeitraum ist abgelaufen.</string>
<string name="api.subsonic.upgrade_client">Inkompatible Versionen. Bitte die Ultrasonic App aktualisieren.</string>
<string name="api.subsonic.upgrade_server">Inkompatible Versionen. Bitte den subsonic Server aktualisieren..</string>
<string name="api.subsonic.upgrade_server">Inkompatible Versionen. Bitte den subsonic Server aktualisieren.</string>
<!-- Subsonic feature flags -->
<string name="feature_flags_category_title">Funktionseinstellungem</string>

View File

@ -28,7 +28,9 @@
<string name="button_bar.playlists">Listas de reproducción</string>
<string name="button_bar.search">Buscar</string>
<string name="chat.send_a_message">Enviar un mensaje</string>
<string name="common.album">Álbum</string>
<string name="common.appname">Ultrasonic</string>
<string name="common.artist">Artista</string>
<string name="common.cancel">Cancelar</string>
<string name="common.comment">Comentario</string>
<string name="common.confirm">Confirmar</string>
@ -48,6 +50,7 @@
<string name="common.play_shuffled">Reproducción aleatoria</string>
<string name="common.public">Public</string>
<string name="common.save">Guardar</string>
<string name="common.title">Título</string>
<string name="common.unpin">Desanclar</string>
<string name="common.various_artists">Varios artistas</string>
<string name="delete_playlist">Quieres eliminar %1$s</string>
@ -279,6 +282,8 @@
<string name="settings.search_title">Configuración de la búsqueda</string>
<string name="settings.send_bluetooth_album_art_summary">Enviar la carátula del álbum vía Bluetooth (Puede causar que las notificaciones Bluetooth fallen)</string>
<string name="settings.send_bluetooth_album_art">Carátula del Álbum vía Bluetooth</string>
<string name="settings.disable_send_now_playing_list_summary">La lista de reproducción actual no se enviará a los dispositivos conectados. Esto puede restaurar la compatibilidad con dispositivos AVRCP 1.3, cuando la visualización de la pista actual no se actualiza</string>
<string name="settings.disable_send_now_playing_list">Desactivar el envío de la lista de reproducción actual</string>
<string name="settings.send_bluetooth_notification_summary">Enviar notificaciones de reproducción vía Bluetooth</string>
<string name="settings.send_bluetooth_notification">Enviar notificaciones Bluetooth</string>
<string name="settings.server_manage_servers">Administrar servidores</string>
@ -310,7 +315,7 @@
<string name="settings.title.allow_self_signed_certificate">Permir certificado HTTPS autofirmado</string>
<string name="settings.title.enable_ldap_users_support">Forzar autenticación de contraseña plana</string>
<string name="settings.summary.enable_ldap_users_support">Esto obliga a la aplicación a enviar siempre la contraseña sin cifrar.
Útil si el servidor Subsonic no admite la nueva API de autenticación para los usuarios..</string>
Útil si el servidor Subsonic no admite la nueva API de autenticación para los usuarios.</string>
<string name="settings.use_folder_for_album_artist">Usar carpetas para el nombre del artista</string>
<string name="settings.use_folder_for_album_artist_summary">Se asume que la carpeta en el nivel mal alto es el nombre del artista del álbum</string>
<string name="settings.use_id3">Navegar usando las etiquetas ID3</string>
@ -344,11 +349,13 @@
<string name="widget.sdcard_missing">No hay tarjeta SD</string>
<string name="settings.share_description_default">Descripción predeterminada al compartir</string>
<string name="settings.sharing_title">Compartiendo</string>
<string name="settings.sharing_always_ask_for_details_summary">Preguntar siempre por la descripción y caducidad cuando se crea un compartido</string>
<string name="settings.sharing_always_ask_for_details_summary">Preguntar siempre por la descripción y caducidad al crear un recurso compartido en el servidor</string>
<string name="settings.sharing_always_ask_for_details">Preguntar siempre los detalles</string>
<string name="settings.share_expiration_default">Tiempo de caducidad predeterminado</string>
<string name="do_not_show_dialog_again">No mostrar el diálogo de nuevo</string>
<string name="share_set_share_options">Configurar las opciones de compartir</string>
<string name="share_on_server">Crear recurso compartido en el servidor </string>
<string name="settings.share_on_server_summary">Compartir creará un recurso compartido en el servidor y compartirá su URL. Si está desactivado, solo se comparten los detalles de la canción</string>
<string name="no_expiration">Sin caducidad</string>
<string name="download.toggle_playlist">Alternar lista de reproducción</string>
<string name="download.bookmark_set">Configurar marcador</string>
@ -371,6 +378,7 @@
<string name="settings.share_expiration">Tiempo de caducidad</string>
<string name="download_song_removed">\"%s\" fue eliminado/a de la lista de reproducción</string>
<string name="download.share_playlist">Compartir lista de reproducción</string>
<string name="download.share_song">Compartir la canción actual</string>
<string name="settings.share_greeting_default">Saludo predeterminado para los compartidos</string>
<string name="share_default_greeting">Echa un vistazo a esta música que te comparto desde %s</string>
<string name="share_via">Compartir canciones vía</string>
@ -395,6 +403,7 @@
<string name="settings.debug.log_keep">Mantener archivos</string>
<string name="settings.debug.log_delete">Borrar archivos</string>
<string name="settings.debug.log_deleted">Archivos de registro eliminados.</string>
<string name="notification.downloading_title">Descargando medios en segundo plano…</string>
<string name="permissions.access_error">Ultrasonic no puede acceder a la caché de los ficheros de música. La ubicación de la caché se restableció a la ruta predeterminada.</string>
<string name="permissions.message_box_title">Atención</string>

View File

@ -344,7 +344,6 @@
<string name="widget.sdcard_missing">Aucune carte SD</string>
<string name="settings.share_description_default">Description par défaut de la collection partagée</string>
<string name="settings.sharing_title">Répartition</string>
<string name="settings.sharing_always_ask_for_details_summary">Toujours demander pour la description et l\'expiration lors de la création d\'une collection partagée</string>
<string name="settings.sharing_always_ask_for_details">Toujours demander pour plus de détails</string>
<string name="settings.share_expiration_default">Temps d\'expiration par défaut</string>
<string name="do_not_show_dialog_again">Ne montre pas de dialogue à nouveau</string>
@ -395,7 +394,6 @@
<string name="settings.debug.log_keep">Conserver les fichiers</string>
<string name="settings.debug.log_delete">Supprimer les fichiers</string>
<string name="settings.debug.log_deleted">Fichiers de log supprimés</string>
<string name="permissions.access_error">Ultrasonic ne peut pas accéder au cache. Le répertoire de cache a été réinitialisé sur le chemin par défaut.</string>
<string name="permissions.message_box_title">Attention</string>
<string name="permissions.permission_missing">Ultrasonic requiert les droits de lecture/écriture sur le répertoire de cache. Le répertoire de cache a été réinitialisé à la valeur par défaut.</string>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="background_task.loading">Betöltés&#8230;</string>
<string name="background_task.network_error">Hálózati hiba történt! Kérjük, ellenőrizze a kiszolgáló címét vagy próbálja később!</string>
@ -315,7 +315,7 @@
<string name="settings.use_id3_summary">ID3 Tag módszer használata a fájlredszer alapú mód helyett.</string>
<string name="settings.show_artist_picture">Előadó képének megjelenítése</string>
<string name="settings.show_artist_picture_summary">Az előadó listában megjeleníti a képeket, amennyiben elérhetőek</string>
<string name="main.video">Videó</string>
<string name="main.video" tools:ignore="UnusedResources">Videó</string>
<string name="settings.view_refresh">Nézet frissítési gyakorisága</string>
<string name="settings.view_refresh_500">.5 másodperc</string>
<string name="settings.view_refresh_1000">1 másodperc</string>
@ -335,14 +335,13 @@
<string name="util.bytes_format.gigabyte">0.00 GB</string>
<string name="util.bytes_format.kilobyte">0 KB</string>
<string name="util.bytes_format.megabyte">0.00 MB</string>
<string name="util.no_time">-:--</string>
<string name="util.no_time" tools:ignore="TypographyDashes">-:--</string>
<string name="util.zero_time">0:00</string>
<string name="widget.initial_text">Érintse meg a zene kiválasztásához</string>
<string name="widget.sdcard_busy">Az SD kártya nem elérhető!</string>
<string name="widget.sdcard_missing">Nincs SD kártya!</string>
<string name="settings.share_description_default">Megosztás alapértelmezett leírása</string>
<string name="settings.sharing_title">Megosztás</string>
<string name="settings.sharing_always_ask_for_details_summary">Megosztás létrehozásakor mindig kérje be a leírást és a lejárati időt.</string>
<string name="settings.sharing_always_ask_for_details">Mindig kérdezzen rá a részletekre</string>
<string name="settings.share_expiration_default">Alapértelmezett lejárati idő</string>
<string name="do_not_show_dialog_again">A párbeszédablak ne jelenjen meg többé</string>
@ -393,7 +392,6 @@
<string name="settings.debug.log_keep">Fájlok megtartása</string>
<string name="settings.debug.log_delete">Fájlok törlése</string>
<string name="settings.debug.log_deleted">Naplófájlok törölve.</string>
<string name="permissions.access_error">Az Ultrasonic nem éri el a zenei fájl gyorsítótárat. A gyorsítótár helye visszaállítva az alapbeállításra.</string>
<string name="permissions.message_box_title">Figyelem</string>
<string name="permissions.permission_missing">Az Ultrasonic működéséhez írás/olvasás hozzáférés szükséges a zenei fájl gyorsítótárhoz. A gyorsítótár helye visszaállítva az alapbeállításra.</string>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="background_task.loading">Caricamento&#8230;</string>
<string name="background_task.network_error">Si è verificato un errore di rete. Si prega di controllare l\'indirizzo del server o riprovare più tardi.</string>
@ -290,7 +290,7 @@
<string name="settings.use_folder_for_album_artist_summary">Presumi che la cartella superiore sia il nome dell\'artista dell\'album</string>
<string name="settings.use_id3">Sfoglia Utilizzando Tag ID3</string>
<string name="settings.use_id3_summary">Usa metodi tag ID3 invece dei metodi basati sul filesystem</string>
<string name="main.video">Video</string>
<string name="main.video" tools:ignore="UnusedResources">Video</string>
<string name="settings.view_refresh_500">.5 secondo</string>
<string name="settings.view_refresh_1000">1 secondo</string>
<string name="settings.view_refresh_1500">1.5 secondi</string>
@ -307,7 +307,7 @@
<string name="util.bytes_format.gigabyte">0.00 GB</string>
<string name="util.bytes_format.kilobyte">0 KB</string>
<string name="util.bytes_format.megabyte">0.00 MB</string>
<string name="util.no_time">-:--</string>
<string name="util.no_time" tools:ignore="TypographyDashes">-:--</string>
<string name="util.zero_time">0:00</string>
<string name="widget.initial_text">Tocca per selezionare musica</string>
<string name="widget.sdcard_busy">Scheda SD non disponibile</string>
@ -324,9 +324,5 @@
<string name="share_comment">Commenta</string>
<string name="download_song_removed">\"%s\" è stato rimosso dalla playlist</string>
<string name="share_via">Condividi canzoni via</string>
<plurals name="select_album_n_songs">
<item quantity="one">1 canzone</item>
<item quantity="other">%d canzoni</item>
</plurals>
<string name="api.subsonic.trial_period_is_over">Il periodo di prova è terminato.</string>
</resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="background_task.loading">Bezig met laden&#8230;</string>
<string name="background_task.network_error">Er is een netwerkfout opgetreden. Controleer het serveradres of probeer het later opnieuw.</string>
@ -28,7 +28,9 @@
<string name="button_bar.playlists">Afspeellijsten</string>
<string name="button_bar.search">Zoeken</string>
<string name="chat.send_a_message">Bericht versturen</string>
<string name="common.album">Album</string>
<string name="common.appname">Ultrasonic</string>
<string name="common.artist">Artiest</string>
<string name="common.cancel">Annuleren</string>
<string name="common.comment">Reageren</string>
<string name="common.confirm">Bevestigen</string>
@ -48,6 +50,7 @@
<string name="common.play_shuffled">Willekeurig afspelen</string>
<string name="common.public">Openbaar</string>
<string name="common.save">Opslaan</string>
<string name="common.title">Titel</string>
<string name="common.unpin">Losmaken</string>
<string name="common.various_artists">Verschillende artiesten</string>
<string name="delete_playlist">Wil je %1$s verwijderen?</string>
@ -110,7 +113,9 @@
<string name="main.songs_starred">Favorieten</string>
<string name="main.songs_title">Nummers</string>
<string name="main.videos">Video\'s</string>
<string name="main.welcome_text_demo">Om in Ultrasonic naar je eigen muziek te luisteren, heb je je <b>eigen server</b> nodig. \n\n➤ Maar als je de app eerst wilt uitproberen, dan kun je voorlopig de demoserver gebruiken. \n\n➤ In de <b>instellingen</b> kun je je eigen server instellen.</string>
<string name="main.welcome_title">Welkom!</string>
<string name="main.welcome_cancel">Open de instellingen</string>
<string name="menu.about">Over</string>
<string name="menu.common">Algemeen</string>
<string name="menu.deleted_playlist">Afspeellijst %s verwijderd</string>
@ -123,7 +128,7 @@
<string name="music_library.label_offline">Offline media</string>
<string name="music_service.retry">Er is een netwerkfout opgetreden. Bezig met opnieuw proberen; poging %1$d van %2$d.</string>
<string name="parser.artist_count">%d artiesten opgehaald.</string>
<string name="parser.reading">Bezig met uitlezen van server...</string>
<string name="parser.reading">Bezig met uitlezen van server</string>
<string name="parser.reading_done">Klaar!</string>
<string name="playlist.label">Afspeellijsten</string>
<string name="playlist.update_info">Informatie bijwerken</string>
@ -148,7 +153,7 @@
<string name="select_artist.folder">Map kiezen</string>
<string name="select_genre.empty">Geen genres gevonden</string>
<string name="select_playlist.empty">Geen opgeslagen afspeellijsten op server</string>
<string name="service.connecting">Bezig met verbinden met server; even geduld...</string>
<string name="service.connecting">Bezig met verbinden met server; even geduld</string>
<string name="settings.appearance_title">Uiterlijk</string>
<string name="settings.buffer_length">Bufferduur</string>
<string name="settings.buffer_length_0">Uitgeschakeld</string>
@ -277,6 +282,8 @@
<string name="settings.search_title">Zoekinstellingen</string>
<string name="settings.send_bluetooth_album_art_summary">Albumhoezen versturen via bluetooth (dit kan leiden tot mislukte bluetoothmeldingen)</string>
<string name="settings.send_bluetooth_album_art">Albumhoezen versturen via bluetooth</string>
<string name="settings.disable_send_now_playing_list_summary">De lijst Nu aan het afspelen wordt niet gedeeld met verbonden apparaten. Hierdoor wordt de comptabiliteit met AVCRP 1.3-apparaten hersteld als het huidige nummer niet wordt bijgewerkt.</string>
<string name="settings.disable_send_now_playing_list">Nu aan het afspelen-lijst niet delen</string>
<string name="settings.send_bluetooth_notification_summary">Afspeelmeldingen sturen via bluetooth</string>
<string name="settings.send_bluetooth_notification">Bluetoothmelding sturen</string>
<string name="settings.server_manage_servers">Manage Servers</string>
@ -315,7 +322,7 @@
<string name="settings.use_id3_summary">ID3-labels gebruiken in plaats van systeemlabels</string>
<string name="settings.show_artist_picture">Artiestfoto tonen op artiestenlijst</string>
<string name="settings.show_artist_picture_summary">Toont de artiestfoto op de artiestenlijst (indien beschikbaar)</string>
<string name="main.video">Video</string>
<string name="main.video" tools:ignore="UnusedResources">Video</string>
<string name="settings.view_refresh">Verversen</string>
<string name="settings.view_refresh_500">0,5 seconden</string>
<string name="settings.view_refresh_1000">1 seconde</string>
@ -335,7 +342,7 @@
<string name="util.bytes_format.gigabyte">0,00 GB</string>
<string name="util.bytes_format.kilobyte">0 KB</string>
<string name="util.bytes_format.megabyte">0,00 MB</string>
<string name="util.no_time">-:--</string>
<string name="util.no_time" tools:ignore="TypographyDashes">-:--</string>
<string name="util.zero_time">0:00</string>
<string name="widget.initial_text">Druk om muziek te selecteren</string>
<string name="widget.sdcard_busy">SD-kaart niet beschikbaar</string>
@ -347,6 +354,8 @@
<string name="settings.share_expiration_default">Standaard vervaltijd</string>
<string name="do_not_show_dialog_again">Niet meer tonen</string>
<string name="share_set_share_options">Deelopties instellen</string>
<string name="share_on_server">Delen op server</string>
<string name="settings.share_on_server_summary">Delen deelt een item op de server en maakt een deel-url aan. Schakel uit om alleen de nummerinformatie te delen.</string>
<string name="no_expiration">Geen vervaldatum</string>
<string name="download.toggle_playlist">Afspeellijst tonen/verbergen</string>
<string name="download.bookmark_set">Bladwijzer instellen</string>
@ -369,6 +378,7 @@
<string name="settings.share_expiration">Tijd tot verval</string>
<string name="download_song_removed">\"%s\" is verwijderd uit de afspeellijst</string>
<string name="download.share_playlist">Afspeellijst delen</string>
<string name="download.share_song">Huidig nummer delen</string>
<string name="settings.share_greeting_default">Standaard deelbericht</string>
<string name="share_default_greeting">Hé, luister eens naar de muziek die ik heb gedeeld via %s</string>
<string name="share_via">Nummers delen via</string>
@ -389,10 +399,11 @@
<string name="settings.debug.title">Foutopsporingsopties</string>
<string name="settings.debug.log_to_file">Foutopsporingslogboek bijhouden</string>
<string name="settings.debug.log_path">De logboeken worden opgeslagen in %1$s/%2$s</string>
<string name="settings.debug.log_summary">Er staan %1$s logboeken in de map \'%3$s\' met een omvang van om en nabij de %2$s MB. Wil je deze behouden?</string>
<string name="settings.debug.log_summary">Er staan %1$s logboeken in de map %3$s met een omvang van om en nabij de %2$s MB. Wil je deze behouden?</string>
<string name="settings.debug.log_keep">Behouden</string>
<string name="settings.debug.log_delete">Verwijderen</string>
<string name="settings.debug.log_deleted">De logboeken zijn verwijderd.</string>
<string name="notification.downloading_title">Bezig met downloaden van media op de achtergrond…</string>
<string name="permissions.access_error">Ultrasonic heeft geen toegang tot de muziekcache. De cachelocatie is teruggezet op de standaardlocatie.</string>
<string name="permissions.message_box_title">Waarschuwing</string>
@ -430,6 +441,7 @@
<string name="server_editor.authentication">Authenticatie</string>
<string name="server_editor.advanced">Geavanceerde instellingen</string>
<string name="server_editor.disabled_feature">Eén of meerdere functies zijn uitgeschakeld omdat de server ze niet ondersteunt.\nVoer deze test later opnieuw uit.</string>
<string name="server_menu.demo">Demoserver</string>
<plurals name="select_album_n_songs">
<item quantity="one">%d nummer</item>
@ -445,19 +457,19 @@
</plurals>
<plurals name="select_album_n_songs_unpinned">
<item quantity="one">%d los te maken nummer geselecteerd.</item>
<item quantity="other">%dlos te maken nummers geselecteerd. </item>
<item quantity="other">%d los te maken nummers geselecteerd.</item>
</plurals>
<plurals name="select_album_n_songs_added">
<item quantity="one">%d nummer toegevoegd aan einde van afspeelwachtrij.</item>
<item quantity="other">%d nummers toegevoegd aan einde van afspeelwachtrij.</item>
<item quantity="one">%d nummer toegevoegd aan het einde van afspeelwachtrij.</item>
<item quantity="other">%d nummers toegevoegd aan het einde van afspeelwachtrij.</item>
</plurals>
<plurals name="select_album_n_songs_play_next">
<item quantity="one">%d nummer ingevoegd na huidige nummer.</item>
<item quantity="other">%d nummers ingevoegd na huidige nummer.</item>
<item quantity="one">%d nummer ingevoegd na het huidige nummer.</item>
<item quantity="other">%d nummers ingevoegd na het huidige nummer.</item>
</plurals>
<plurals name="select_album_donate_dialog_n_trial_days_left">
<item quantity="one">Nog %d dag over van proefperiode</item>
<item quantity="other">Nog %d dagen over van proefperiode</item>
<item quantity="one">Nog %d dag over van de proefperiode</item>
<item quantity="other">Nog %d dagen over van de proefperiode</item>
</plurals>
<!-- Subsonic api errors -->

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="background_task.loading">Ładowanie&#8230;</string>
<string name="background_task.network_error">Wystąpił błąd sieci. Proszę sprawdzić adres serwera i spróbować później.</string>
@ -298,7 +298,7 @@ ponieważ api Subsonic nie wspiera nowego sposobu autoryzacji dla użytkowników
<string name="settings.use_folder_for_album_artist_summary">Zakłada, że folder najwyższego poziomu jest nazwą artysty albumu</string>
<string name="settings.use_id3">Przeglądaj używając tagów ID3</string>
<string name="settings.use_id3_summary">Używa metod z tagów ID3 zamiast metod opartych na systemie plików</string>
<string name="main.video">Wideo</string>
<string name="main.video" tools:ignore="UnusedResources">Wideo</string>
<string name="settings.view_refresh">Odświeżanie widoku</string>
<string name="settings.view_refresh_500">co pół sekundy</string>
<string name="settings.view_refresh_1000">co 1 sekundę</string>
@ -318,14 +318,13 @@ ponieważ api Subsonic nie wspiera nowego sposobu autoryzacji dla użytkowników
<string name="util.bytes_format.gigabyte">0.00 GB</string>
<string name="util.bytes_format.kilobyte">0 KB</string>
<string name="util.bytes_format.megabyte">0.00 MB</string>
<string name="util.no_time">-:--</string>
<string name="util.no_time" tools:ignore="TypographyDashes">-:--</string>
<string name="util.zero_time">0:00</string>
<string name="widget.initial_text">Dotknij, aby wybrać muzykę</string>
<string name="widget.sdcard_busy">Karta SD jest niedostępna</string>
<string name="widget.sdcard_missing">Brak karty SD</string>
<string name="settings.share_description_default">Domyślne ustawienia udostępniania</string>
<string name="settings.sharing_title">Udostępnianie</string>
<string name="settings.sharing_always_ask_for_details_summary">Zawsze pytaj o opis i czas wygaśnięcia podczas tworzenia udostępnienia</string>
<string name="settings.sharing_always_ask_for_details">Zawsze pytaj o szczegóły</string>
<string name="settings.share_expiration_default">Domyślny czas wygaśnięcia</string>
<string name="do_not_show_dialog_again">Nie wyświetlaj więcej tego okna</string>
@ -370,40 +369,40 @@ ponieważ api Subsonic nie wspiera nowego sposobu autoryzacji dla użytkowników
<item quantity="other">%d utworów</item>
</plurals>
<plurals name="select_album_n_songs_pinned">
<item quantity="one">%d utwór zaznaczony do przypięcia</item>
<item quantity="few">%d utwory zaznaczone do przypięcia</item>
<item quantity="many">%d utworów zaznaczonych do przypięcia</item>
<item quantity="other">%d utworów zaznaczonych do przypięcia</item>
<item quantity="one">%d utwór zaznaczony do przypięcia.</item>
<item quantity="few">%d utwory zaznaczone do przypięcia.</item>
<item quantity="many">%d utworów zaznaczonych do przypięcia.</item>
<item quantity="other">%d utworów zaznaczonych do przypięcia.</item>
</plurals>
<plurals name="select_album_n_songs_downloaded">
<item quantity="one">%d utwór zaznaczony do pobrania</item>
<item quantity="few">%d utwory zaznaczone do pobrania</item>
<item quantity="many">%d utworów zaznaczonych do pobrania</item>
<item quantity="other">%d utworów zaznaczonych do pobrania</item>
<item quantity="one">%d utwór zaznaczony do pobrania.</item>
<item quantity="few">%d utwory zaznaczone do pobrania.</item>
<item quantity="many">%d utworów zaznaczonych do pobrania.</item>
<item quantity="other">%d utworów zaznaczonych do pobrania.</item>
</plurals>
<plurals name="select_album_n_songs_unpinned">
<item quantity="one">%d utwór zaznaczony do odpięcia</item>
<item quantity="few">%d utwory zaznaczone do odpięcia</item>
<item quantity="many">%d utworów zaznaczonych do odpięcia </item>
<item quantity="other">%d utworów zaznaczonych do odpięcia</item>
<item quantity="one">%d utwór zaznaczony do odpięcia.</item>
<item quantity="few">%d utwory zaznaczone do odpięcia.</item>
<item quantity="many">%d utworów zaznaczonych do odpięcia.</item>
<item quantity="other">%d utworów zaznaczonych do odpięcia.</item>
</plurals>
<plurals name="select_album_n_songs_added">
<item quantity="one">%d utwór dodany na koniec kolejki odtwarzania</item>
<item quantity="few">%d utwory dodane na koniec kolejki odtwarzania</item>
<item quantity="many">%dutworów dodanych na koniec kolejki odtwarzania </item>
<item quantity="other">%d utworów dodanych na koniec kolejki odtwarzania </item>
<item quantity="one">%d utwór dodany na koniec kolejki odtwarzania.</item>
<item quantity="few">%d utwory dodane na koniec kolejki odtwarzania.</item>
<item quantity="many">%d utworów dodanych na koniec kolejki odtwarzania.</item>
<item quantity="other">%d utworów dodanych na koniec kolejki odtwarzania.</item>
</plurals>
<plurals name="select_album_n_songs_play_next">
<item quantity="one">%d utwór wstawiony po bieżącym utworze</item>
<item quantity="few">%d utwory wstawione po bieżącym utworze</item>
<item quantity="many">%d utworów wstawionych po bieżącym utworze </item>
<item quantity="other">%d utworów wstawionych po bieżącym utworze </item>
<item quantity="one">%d utwór wstawiony po bieżącym utworze.</item>
<item quantity="few">%d utwory wstawione po bieżącym utworze.</item>
<item quantity="many">%d utworów wstawionych po bieżącym utworze.</item>
<item quantity="other">%d utworów wstawionych po bieżącym utworze.</item>
</plurals>
<plurals name="select_album_donate_dialog_n_trial_days_left">
<item quantity="one">%d dzień pozostał do zakończenia okresu próbnego</item>
<item quantity="few">%d dni pozostały do zakończenia okresu próbnego</item>
<item quantity="many">%d dni pozostało do zakończenia okresu próbnego </item>
<item quantity="other">%d dni pozostało do zakończenia okresu próbnego </item>
<item quantity="many">%d dni pozostało do zakończenia okresu próbnego</item>
<item quantity="other">%d dni pozostało do zakończenia okresu próbnego</item>
</plurals>
<!-- Subsonic api errors -->

View File

@ -206,14 +206,14 @@
<string name="settings.directory_cache_time_5">5 minutos</string>
<string name="settings.directory_cache_time_60">1 hora</string>
<string name="settings.disc_sort">Classificar Músicas por Álbum</string>
<string name="settings.disc_sort_summary">Classificar músicas pelo número do álbum e faixas.</string>
<string name="settings.disc_sort_summary">Classificar músicas pelo número do álbum e faixas</string>
<string name="settings.display_bitrate">Mostrar Taxa de Bits e Sufixo de Arquivo</string>
<string name="settings.display_bitrate_summary">Adicionar o nome do artista com a taxa de bits e sufixo do arquivo</string>
<string name="settings.download_transition">Mostrar Downloads na Reprodução</string>
<string name="settings.download_transition_summary">Transição para atividade de download quando iniciar reprodução</string>
<string name="settings.gapless_playback">Reprodução sem Interrupção</string>
<string name="settings.gapless_playback_summary">Ativar reprodução sem interrupção</string>
<string name="settings.hide_media_summary">Esconder músicas de outros aplicativos.</string>
<string name="settings.hide_media_summary">Esconder arquivos de músicas de outros aplicativos</string>
<string name="settings.hide_media_title">Esconder de Outros</string>
<string name="settings.hide_media_toast">Será efetivado na próxima vez que o Android procurar por músicas em seu celular.</string>
<string name="settings.increment_time">Intervalo de Salto</string>
@ -231,9 +231,9 @@
<string name="settings.max_bitrate_64">64 Kbps</string>
<string name="settings.max_bitrate_80">80 Kbps</string>
<string name="settings.max_bitrate_96">96 Kbps</string>
<string name="settings.max_bitrate_mobile">Máx. de Taxa de Bits - Celular</string>
<string name="settings.max_bitrate_mobile">Taxa Máxima de Bits - Celular</string>
<string name="settings.max_bitrate_unlimited">Ilimitado</string>
<string name="settings.max_bitrate_wifi">Máx. de Taxa de Bits - Wi-Fi</string>
<string name="settings.max_bitrate_wifi">Taxa Máxima de Bits - Wi-Fi</string>
<string name="settings.max_songs">Máximo de Músicas</string>
<string name="settings.media_button_summary">Obedecer aos botões do celular, fones e botões de mídia do Bluetooth</string>
<string name="settings.media_button_title">Botões de Mídia</string>
@ -258,9 +258,9 @@
<string name="settings.preload_5">5 músicas</string>
<string name="settings.preload_unlimited">Ilimitado</string>
<string name="settings.playback.resume_play_on_headphones_plug.title">Retomar ao Inserir Fone de Ouvido</string>
<string name="settings.playback.resume_play_on_headphones_plug.summary">O aplicativo retomará a reprodução em pausa na inserção dos fones de ouvido no dispositivo.</string>
<string name="settings.playback.resume_play_on_headphones_plug.summary">O aplicativo retomará a reprodução em pausa na inserção dos fones de ouvido no dispositivo</string>
<string name="settings.scrobble_summary">Lembre-se de configurar usuário e senha nos serviços Scrobble do servidor</string>
<string name="settings.scrobble_title">Registre Minhas Músicas</string>
<string name="settings.scrobble_title">Registrar Minhas Músicas</string>
<string name="settings.search_1">1</string>
<string name="settings.search_10">10</string>
<string name="settings.search_100">100</string>
@ -279,6 +279,8 @@
<string name="settings.search_title">Configurações de Pesquisa</string>
<string name="settings.send_bluetooth_album_art_summary">Enviar a arte do álbum via Bluetooth (Pode causar falhas nas notificações do Bluetooth)</string>
<string name="settings.send_bluetooth_album_art">Arte do Álbum via Bluetooth</string>
<string name="settings.disable_send_now_playing_list_summary">A Lista Tocando Agora não será enviada aos dispositivos conectados. Isso pode restaurar a compatibilidade com dispositivos AVRCP 1.3 quando a exibição da trilha atual não é atualizada</string>
<string name="settings.disable_send_now_playing_list">Desativar Envio da Lista Tocando Agora</string>
<string name="settings.send_bluetooth_notification_summary">Enviar notificações de reprodução via Bluetooth</string>
<string name="settings.send_bluetooth_notification">Notificações via Bluetooth</string>
<string name="settings.server_manage_servers">Gerenciar Servidores</string>
@ -344,7 +346,6 @@
<string name="widget.sdcard_missing">Sem cartão SD</string>
<string name="settings.share_description_default">Descrição Padrão do Compartilhamento</string>
<string name="settings.sharing_title">Compartilhamento</string>
<string name="settings.sharing_always_ask_for_details_summary">Sempre perguntar pela descrição e expiração ao criar um compartilhamento</string>
<string name="settings.sharing_always_ask_for_details">Sempre Perguntar por Detalhes</string>
<string name="settings.share_expiration_default">Tempo Padrão para Expirar</string>
<string name="do_not_show_dialog_again">Não mostrar este diálogo novamente</string>
@ -395,6 +396,7 @@
<string name="settings.debug.log_keep">Manter arquivos</string>
<string name="settings.debug.log_delete">Excluir arquivos</string>
<string name="settings.debug.log_deleted">Arquivos de log excluídos.</string>
<string name="notification.downloading_title">Baixado mídia em segundo plano…</string>
<string name="permissions.access_error">O Ultrasonic não pôde acessar o cache dos arquivos de música. O local do cache foi redefinido para o caminho padrão.</string>
<string name="permissions.message_box_title">Atenção</string>
@ -419,9 +421,9 @@
<string name="filepicker.default">Usar o padrão</string>
<string name="filepicker.available_drives">Unidades disponíveis:</string>
<string name="server_selector.label">Servidores configurados</string>
<string name="server_selector.label">Servidores Configurados</string>
<string name="server_selector.delete_confirmation">Quer realmente excluir o servidor?</string>
<string name="server_editor.label">Gerenciar servidor</string>
<string name="server_editor.label">Gerenciar Servidor</string>
<string name="server_editor.new_label">Adicionar Servidor</string>
<string name="server_editor.leave_confirmation">Quer realmente sair e descartar as alterações?</string>
<string name="server_editor.required">Este campo é necessário</string>
@ -447,20 +449,20 @@
<item quantity="other">%d músicas selecionadas para serem baixadas.</item>
</plurals>
<plurals name="select_album_n_songs_unpinned">
<item quantity="one">%d música selecionada para ser desafixada.</item>
<item quantity="one">%d música selecionada para ser desfixada.</item>
<item quantity="other">%d músicas selecionadas para serem desfixadas.</item>
</plurals>
<plurals name="select_album_n_songs_added">
<item quantity="one">%d música adicionada ao fim da fila.</item>
<item quantity="other">%d músicas adicionadas ao fim da fila.</item>
<item quantity="one">%d música adicionada ao final da playlist.</item>
<item quantity="other">%d músicas adicionadas ao final da playlist.</item>
</plurals>
<plurals name="select_album_n_songs_play_next">
<item quantity="one">%d música inserida após a atual.</item>
<item quantity="other">%d músicas inseridas após a atual.</item>
<item quantity="one">%d música adicionada após a atual.</item>
<item quantity="other">%d músicas adicionadas após a atual.</item>
</plurals>
<plurals name="select_album_donate_dialog_n_trial_days_left">
<item quantity="one">Resta %d dia para o fim do período de teste</item>
<item quantity="other">Restam %d dias para o fim do período de teste</item>
<item quantity="one">%d dia restante do período de teste</item>
<item quantity="other">%d dias restantes do período de teste</item>
</plurals>
<!-- Subsonic api errors -->
@ -478,8 +480,8 @@
<!-- Subsonic feature flags -->
<string name="feature_flags_category_title">Sinalização de Recursos</string>
<string name="feature_flags_five_star_rating_title">Usar Classif. 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
em vez de simplesmente estrelar/não estrelar itens.
<string name="feature_flags_five_star_rating_description">Usar o sistema de classificação de 5 estrelas para músicas
em vez de simplesmente estrelar/não estrelar itens
</string>
</resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="background_task.loading">Carregando&#8230;</string>
<string name="background_task.network_error">Ocorreu um erro de rede. Verifique o endereço do servidor ou tente mais tarde.</string>
@ -298,7 +298,7 @@
<string name="settings.use_folder_for_album_artist_summary">Assumir que a pasta mais acima é o nome do artista</string>
<string name="settings.use_id3">Navegar Usando Etiquetas ID3</string>
<string name="settings.use_id3_summary">Usa as etiquetas ID3 ao invés do sistema de ficheiros</string>
<string name="main.video">Vídeo</string>
<string name="main.video" tools:ignore="UnusedResources">Vídeo</string>
<string name="settings.view_refresh">Atualização do Ecrã</string>
<string name="settings.view_refresh_500">.5 segundos</string>
<string name="settings.view_refresh_1000">1 segundo</string>
@ -318,14 +318,13 @@
<string name="util.bytes_format.gigabyte">0.00 GB</string>
<string name="util.bytes_format.kilobyte">0 KB</string>
<string name="util.bytes_format.megabyte">0.00 MB</string>
<string name="util.no_time">&#8212;:&#8212;&#8212;</string>
<string name="util.no_time" tools:ignore="TypographyDashes">&#8212;:&#8212;&#8212;</string>
<string name="util.zero_time">0:00</string>
<string name="widget.initial_text">Toque para selecionar a música</string>
<string name="widget.sdcard_busy">Cartão SD indisponível</string>
<string name="widget.sdcard_missing">Sem cartão SD</string>
<string name="settings.share_description_default">Texto Padrão</string>
<string name="settings.sharing_title">Compartilhamento</string>
<string name="settings.sharing_always_ask_for_details_summary">Sempre pergunta pela descrição e prazo ao criar um compartilhamento</string>
<string name="settings.sharing_always_ask_for_details">Sempre Perguntar por Detalhes</string>
<string name="settings.share_expiration_default">Tempo Padrão para Expirar</string>
<string name="do_not_show_dialog_again">Não mostrar este diálogo novamente</string>
@ -371,24 +370,24 @@
<item quantity="other">%d músicas selecionadas para serem fixadas.</item>
</plurals>
<plurals name="select_album_n_songs_downloaded">
<item quantity="one">%d música selecionada para descarregar.</item>
<item quantity="other">%d músicas selecionadas para serem descarregadas.</item>
<item quantity="one">%d música selecionada para ser baixada.</item>
<item quantity="other">%d músicas selecionadas para serem baixadas.</item>
</plurals>
<plurals name="select_album_n_songs_unpinned">
<item quantity="one">%d música selecionada para ser desafixada.</item>
<item quantity="one">%d música selecionada para ser desfixada.</item>
<item quantity="other">%d músicas selecionadas para serem desfixadas.</item>
</plurals>
<plurals name="select_album_n_songs_added">
<item quantity="one">%d música adicionada ao fim da fila.</item>
<item quantity="other">%d músicas adicionadas ao fim da fila.</item>
<item quantity="one">%d música adicionada ao final da playlist.</item>
<item quantity="other">%d músicas adicionadas ao final da playlist.</item>
</plurals>
<plurals name="select_album_n_songs_play_next">
<item quantity="one">%d música inserida após a atual.</item>
<item quantity="other">%d músicas inseridas após a atual.</item>
<item quantity="one">%d música adicionada após a atual.</item>
<item quantity="other">%d músicas adicionadas após a atual.</item>
</plurals>
<plurals name="select_album_donate_dialog_n_trial_days_left">
<item quantity="one">Resta %d dia para o fim do período de teste</item>
<item quantity="other">Restam %d dias para o fim do período de teste</item>
<item quantity="one">%d dia restante do período de teste</item>
<item quantity="other">%d dias restantes do período de teste</item>
</plurals>
<!-- Subsonic api errors -->

View File

@ -344,7 +344,6 @@
<string name="widget.sdcard_missing">Нет SD-карты</string>
<string name="settings.share_description_default">Описание общего ресурса</string>
<string name="settings.sharing_title">Поделиться</string>
<string name="settings.sharing_always_ask_for_details_summary">Всегда запрашивайте описание и срок действия при создании ресурса</string>
<string name="settings.sharing_always_ask_for_details">Всегда спрашивайте подробности</string>
<string name="settings.share_expiration_default">Время истечения по умолчанию</string>
<string name="do_not_show_dialog_again">Больше не показывать диалог</string>
@ -395,7 +394,6 @@
<string name="settings.debug.log_keep">Сохранить файлы</string>
<string name="settings.debug.log_delete">Удалить файлы</string>
<string name="settings.debug.log_deleted">Удаленные файлы журналов.</string>
<string name="permissions.access_error">Ultrasonic не может получить доступ к кэшу музыкальных файлов. Местоположение кэша было сброшено на путь по умолчанию.</string>
<string name="permissions.message_box_title">Внимание</string>
<string name="permissions.permission_missing">Ultrasonic требуется разрешение на чтение/запись в директории музыкального кэша. Каталог кэша был сброшен на значение по умолчанию.</string>

View File

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="background_task.loading">加载中&#8230;</string>
<string name="background_task.network_error">发生网络错误。请检查服务器地址或稍后重试。</string>
<string name="background_task.unsupported_api">服务端 api v%1$s 不支持此功能。</string>
<string name="background_task.no_network">此软件需要连接网络请打开Wi-Fi或移动网络。</string>
<string name="background_task.not_found">未找到资源,请检查服务器地址。</string>
<string name="background_task.parse_error">未知回复内容,请检查服务器地址</string>
<string name="background_task.ssl_cert_error">HTTPS 证书错误: %1$s.</string>
<string name="background_task.ssl_error">SSL连接异常。请检查服务器证书。</string>
<string name="background_task.wait">请稍等&#8230;</string>
@ -14,6 +15,13 @@
<string name="button_bar.chat">聊天</string>
<string name="button_bar.home">Ultrasonic 主页</string>
<string name="button_bar.now_playing">正在播放</string>
<string name="buttons.play">播放</string>
<string name="buttons.pause">暂停</string>
<string name="buttons.repeat">重复播放</string>
<string name="buttons.shuffle">随机播放</string>
<string name="buttons.stop">停止</string>
<string name="buttons.next">下一首</string>
<string name="buttons.previous">上一首</string>
<string name="podcasts.label">播客</string>
<string name="podcasts_channels.empty">没有已注册的播客频道</string>
<string name="button_bar.podcasts">播客</string>
@ -31,16 +39,26 @@
<string name="common.name">名称</string>
<string name="common.ok">确定</string>
<string name="common.pin">固定</string>
<string name="common.pause">暂停</string>
<string name="common.play">播放</string>
<string name="common.play_last">最后一首</string>
<string name="common.play_next">下一首</string>
<string name="common.play_previous">上一首</string>
<string name="common.play_now">现在播放</string>
<string name="common.play_shuffled">随机播放</string>
<string name="common.public">公开</string>
<string name="common.save">保存</string>
<string name="common.unpin">取消固定</string>
<string name="common.various_artists">群星</string>
<string name="delete_playlist">确定要删除 %1$s吗</string>
<string name="download.bookmark_removed" formatted="false">书签已删除。</string>
<string name="download.bookmark_set_at_position" formatted="false">书签设置为 %s。</string>
<string name="download.empty">空的播放列表</string>
<string name="download.jukebox_not_authorized">不允许远程控制. 请在您的服务器上的 <b>Users &gt; Settings</b> 打开点唱机模式。</string>
<string name="download.jukebox_off">关闭远程控制,音乐将在手机上播放</string>
<string name="download.jukebox_offline">离线模式不支持远程控制</string>
<string name="download.jukebox_on">打开远程控制,音乐将在服务端播放。</string>
<string name="download.jukebox_server_too_old">远程控制不支持,请升级您的 Subsonic服务器。</string>
<string name="download.menu_equalizer">均衡器</string>
<string name="download.menu_jukebox_off">关闭 Jukebox</string>
<string name="download.menu_jukebox_on">开启 Jukebox</string>
@ -49,19 +67,29 @@
<string name="download.menu_save">保存播放列表</string>
<string name="download.menu_screen_off">关闭屏幕常亮</string>
<string name="download.menu_screen_on">开启屏幕常亮</string>
<string name="download.menu_show_album">显示专辑</string>
<string name="download.menu_shuffle">随机</string>
<string name="download.menu_shuffle_notification">已随机排列播放列表</string>
<string name="download.menu_visualizer">可视化</string>
<string name="download.playerstate_buffering">缓冲中</string>
<string name="download.playerstate_downloading">下载中 - %s</string>
<string name="download.playerstate_playing_shuffle">随机播放</string>
<string name="download.playlist_done">已成功保存播放列表。</string>
<string name="download.playlist_error">保存播放列表失败,请重试。</string>
<string name="download.playlist_name">输入播放列表名称:</string>
<string name="download.playlist_saving">正在保存播放列表 - \"%s\"&#8230;</string>
<string name="download.playlist_title">保存播放列表</string>
<string name="download.repeat_all">循环播放所有</string>
<string name="download.repeat_off">关闭循环播放</string>
<string name="download.repeat_single">循环播放</string>
<string name="download.visualizer_off">关闭可视化</string>
<string name="download.visualizer_on">打开可视化</string>
<string name="equalizer.enabled">开启</string>
<string name="equalizer.label">均衡器</string>
<string name="equalizer.preset">选择预设</string>
<string name="error.label">错误</string>
<string name="help.url">file:///android_asset/html/en/index.html</string>
<string name="jukebox.is_default">默认自动点唱机</string>
<string name="lyrics.nomatch">找不到歌词</string>
<string name="main.albums_alphaByArtist">按艺术家排序</string>
<string name="main.albums_alphaByName">按名称排序</string>
@ -81,8 +109,11 @@
<string name="main.songs_starred">收藏夹</string>
<string name="main.songs_title">歌曲</string>
<string name="main.videos">视频</string>
<string name="main.welcome_text_demo">使用Ultrasonic来播放您的音乐需要您自己的 <b>服务器</b>。\n\n➤ 如果您想试试此应用, 可以添加试用服务器。 \n\n➤ 可在 <b>设置</b>中编辑服务器配置信息。</string>
<string name="main.welcome_title">欢迎!</string>
<string name="main.welcome_cancel">点击前往设置</string>
<string name="menu.about">关于</string>
<string name="menu.common">公共</string>
<string name="menu.deleted_playlist">已删除播放列表 %s</string>
<string name="menu.deleted_playlist_error">播放列表删除失败%s</string>
<string name="menu.exit">退出</string>
@ -92,10 +123,13 @@
<string name="music_library.label">媒体库</string>
<string name="music_library.label_offline">离线媒体</string>
<string name="music_service.retry">发生网络错误,正在重试 %1$d of %2$d.</string>
<string name="parser.artist_count">有 %d 位艺术家。</string>
<string name="parser.reading">正在加载服务器。</string>
<string name="parser.reading_done">正在加载服务器。完成!</string>
<string name="playlist.label">播放列表</string>
<string name="playlist.update_info">更新信息</string>
<string name="playlist.updated_info">已更新此播放列表信息 - %s</string>
<string name="playlist.updated_info_error">更新播放列表信息失败 - %s</string>
<string name="progress.wait">请稍等&#8230;</string>
<string name="search.albums">专辑</string>
<string name="search.artists">艺人</string>
@ -106,6 +140,8 @@
<string name="search.songs">歌曲</string>
<string name="search.title">搜索</string>
<string name="select_album.empty">找不到歌曲</string>
<string name="select_album.n_selected">已选择 %d 首曲目。</string>
<string name="select_album.n_unselected">未选择 %d 首曲目。</string>
<string name="select_album.no_network">警告:网络不可用</string>
<string name="select_album.no_sdcard">错误没有SD卡</string>
<string name="select_album.play_all">播放所有</string>
@ -113,7 +149,9 @@
<string name="select_artist.folder">选择文件夹</string>
<string name="select_genre.empty">找不到流派</string>
<string name="select_playlist.empty">服务器上没有保存的播放列表</string>
<string name="service.connecting">服务器连接中,请稍等。</string>
<string name="settings.appearance_title">外观</string>
<string name="settings.buffer_length">缓冲长度</string>
<string name="settings.buffer_length_0">已禁用</string>
<string name="settings.buffer_length_1">1 秒</string>
<string name="settings.buffer_length_10">10 秒</string>
@ -147,9 +185,18 @@
<string name="settings.cache_size_8000">8 GB</string>
<string name="settings.cache_size_9000">9 GB</string>
<string name="settings.cache_size_unlimited">不限制</string>
<string name="settings.cache_title">音乐缓存</string>
<string name="settings.chat_refresh">聊天消息刷新时间间隔</string>
<string name="settings.clear_bookmark">清空书签</string>
<string name="settings.clear_bookmark_summary">歌曲播放完毕后清除书签</string>
<string name="settings.clear_playlist">清空播放列表</string>
<string name="settings.clear_playlist_summary">所有歌曲播放完毕后清空播放列表</string>
<string name="settings.clear_search_history">清空搜索历史</string>
<string name="settings.connection_failure">连接失败</string>
<string name="settings.default_albums">默认专辑</string>
<string name="settings.default_artists">默认艺术家</string>
<string name="settings.default_songs">默认音乐</string>
<string name="settings.directory_cache_time">目录缓存时间</string>
<string name="settings.directory_cache_time_0">已禁用</string>
<string name="settings.directory_cache_time_1">1 分钟</string>
<string name="settings.directory_cache_time_10">10 分钟</string>
@ -157,7 +204,20 @@
<string name="settings.directory_cache_time_30">30 分钟</string>
<string name="settings.directory_cache_time_5">5 分钟</string>
<string name="settings.directory_cache_time_60">1 小时</string>
<string name="settings.disc_sort">按光盘排序歌曲</string>
<string name="settings.disc_sort_summary">按光盘编号和曲目编号对歌曲列表进行排序</string>
<string name="settings.display_bitrate">展示比特率和文件后缀</string>
<string name="settings.display_bitrate_summary">在艺术家姓名后追加比特率和文件后缀</string>
<string name="settings.gapless_playback">无缝播放</string>
<string name="settings.gapless_playback_summary">启用无缝播放</string>
<string name="settings.hide_media_summary">隐藏来自其他应用的音乐</string>
<string name="settings.hide_media_title">隐藏其他来源</string>
<string name="settings.hide_media_toast">在安卓系统下次扫描音乐时生效。</string>
<string name="settings.increment_time">快进间隔</string>
<string name="settings.invalid_url">请填写有效的URL。</string>
<string name="settings.invalid_username">请填写有效用户名 (请去除尾部空格)。</string>
<string name="settings.max_albums">最大专辑</string>
<string name="settings.max_artists">最大艺术家</string>
<string name="settings.max_bitrate_112">112 Kbps</string>
<string name="settings.max_bitrate_128">128 Kbps</string>
<string name="settings.max_bitrate_160">160 Kbps</string>
@ -171,6 +231,10 @@
<string name="settings.max_bitrate_mobile">最大比特率-移动网络</string>
<string name="settings.max_bitrate_unlimited">不限制</string>
<string name="settings.max_bitrate_wifi">最大比特率-WIFI</string>
<string name="settings.max_songs">最大歌曲</string>
<string name="settings.media_button_summary">响应手机、耳机和蓝牙设备的媒体按钮</string>
<string name="settings.media_button_title">媒体按钮</string>
<string name="settings.network_timeout">网络超时</string>
<string name="settings.network_timeout_105000">105 秒</string>
<string name="settings.network_timeout_120000">120 秒</string>
<string name="settings.network_timeout_15000">15 秒</string>
@ -182,6 +246,7 @@
<string name="settings.notifications_title">通知</string>
<string name="settings.network_title">网络</string>
<string name="settings.other_title">其他设置</string>
<string name="settings.playback_control_title">播放控制设置</string>
<string name="settings.preload">预加载歌曲数量</string>
<string name="settings.preload_1">1 首歌</string>
<string name="settings.preload_10">10 首歌</string>
@ -189,6 +254,9 @@
<string name="settings.preload_3">3 首歌</string>
<string name="settings.preload_5">5 首歌</string>
<string name="settings.preload_unlimited">不限制</string>
<string name="settings.playback.resume_play_on_headphones_plug.title">插入耳机时恢复播放</string>
<string name="settings.playback.resume_play_on_headphones_plug.summary">应用将在有线耳机插入设备时恢复已暂停的播放。</string>
<string name="settings.scrobble_summary">请记得在服务器上的 Scrobble 服务中设置您的用户名和密码</string>
<string name="settings.search_1">1</string>
<string name="settings.search_10">10</string>
<string name="settings.search_100">100</string>
@ -205,20 +273,49 @@
<string name="settings.search_75">75</string>
<string name="settings.search_history_cleared">搜索记录已清除</string>
<string name="settings.search_title">搜索设置</string>
<string name="settings.send_bluetooth_album_art_summary">通过蓝牙发送专辑封面(可能导致蓝牙通知失败)</string>
<string name="settings.send_bluetooth_album_art">通过蓝牙发送专辑封面</string>
<string name="settings.disable_send_now_playing_list_summary">现在播放列表不会发送到已连接的设备。 当前曲目显示未更新时这可能会恢复AVRCP 1.3的设备的兼容性。</string>
<string name="settings.disable_send_now_playing_list">禁用发送正在播放列表</string>
<string name="settings.send_bluetooth_notification_summary">通过蓝牙发送播放通知</string>
<string name="settings.send_bluetooth_notification">发送蓝牙通知</string>
<string name="settings.server_manage_servers">管理服务器</string>
<string name="settings.server_address">服务器地址</string>
<string name="settings.server_name">名称</string>
<string name="settings.server_password">密码</string>
<string name="settings.server_remove_server">删除服务器</string>
<string name="settings.server_scaling_summary">从服务器下载缩放图像而不是全尺寸(节省数据流量)</string>
<string name="settings.server_scaling_title">服务器端专辑图片缩放</string>
<string name="settings.server_unused">未启用</string>
<string name="settings.server_username">用户名</string>
<string name="settings.show_lockscreen_controls">锁屏显示控制器</string>
<string name="settings.show_lockscreen_controls_summary">在锁定屏幕上显示播放控件</string>
<string name="settings.show_notification">显示通知</string>
<string name="settings.show_notification_always">总是显示通知</string>
<string name="settings.show_notification_always_summary">当播放列表有音乐时,总是在通知栏显示播放信息</string>
<string name="settings.show_notification_summary">在状态栏中显示正在播放通知</string>
<string name="settings.show_now_playing">显示正在播放</string>
<string name="settings.show_now_playing_summary">在所有活动页面显示正在播放信息</string>
<string name="settings.show_track_number">显示曲目编号</string>
<string name="settings.show_track_number_summary">显示歌曲时包括曲目编号</string>
<string name="settings.test_connection_title">测试连接</string>
<string name="settings.testing_ok">连接正常</string>
<string name="settings.testing_unlicensed">连接正常, 服务器未授权。</string>
<string name="settings.theme_light">Light</string>
<string name="settings.theme_dark">Dark</string>
<string name="settings.theme_black">Black</string>
<string name="settings.theme_title">主题</string>
<string name="settings.title.allow_self_signed_certificate">允许自签名 HTTPS 证书</string>
<string name="main.video">视频</string>
<string name="settings.title.enable_ldap_users_support">强制原始密码认证</string>
<string name="settings.summary.enable_ldap_users_support">这会强制应用始终以未加密的方式发送密码。
如果 Subsonic 服务器不支持新的用户身份验证 API则很有用。</string>
<string name="settings.use_folder_for_album_artist">将艺术家名称作为文件夹</string>
<string name="settings.use_folder_for_album_artist_summary">将艺术家名称作为顶层文件夹名</string>
<string name="settings.use_id3">使用 ID3 标签浏览</string>
<string name="settings.use_id3_summary">使用 ID3 标签方法而不是基于文件系统的方法</string>
<string name="settings.show_artist_picture">在艺术家列表中显示艺术家图片</string>
<string name="settings.show_artist_picture_summary">如果可用,在艺术家列表中显示艺术家图片</string>
<string name="main.video" tools:ignore="UnusedResources">视频</string>
<string name="settings.view_refresh">刷新视图</string>
<string name="settings.view_refresh_500">.5 秒</string>
<string name="settings.view_refresh_1000">1 秒</string>
@ -232,18 +329,33 @@
<string name="settings.view_refresh_5000">5 秒</string>
<string name="settings.wifi_required_summary">仅在连接到 WIFI 时使用流媒体</string>
<string name="settings.wifi_required_title">仅使用 WIFI</string>
<string name="song_details.all">%1$s%2$s</string>
<string name="song_details.kbps">%d kbps</string>
<string name="util.bytes_format.byte">0 B</string>
<string name="util.bytes_format.gigabyte">0.00 GB</string>
<string name="util.bytes_format.kilobyte">0 KB</string>
<string name="util.bytes_format.megabyte">0.00 MB</string>
<string name="util.no_time" tools:ignore="TypographyDashes">-:--</string>
<string name="util.zero_time">0:00</string>
<string name="widget.initial_text">轻触选择音乐</string>
<string name="widget.sdcard_busy">SD 卡不可用</string>
<string name="widget.sdcard_missing">没有 SD 卡</string>
<string name="settings.share_description_default">默认分享说明</string>
<string name="settings.sharing_title">分享</string>
<string name="settings.sharing_always_ask_for_details">始终询问详细信息</string>
<string name="settings.share_expiration_default">默认有效期</string>
<string name="do_not_show_dialog_again">不再显示此对话框</string>
<string name="share_set_share_options">设置分享选项</string>
<string name="no_expiration">无期限</string>
<string name="download.toggle_playlist">切换播放列表</string>
<string name="download.bookmark_set">设为书签</string>
<string name="download.bookmark_delete">删除书签</string>
<string name="download.menu_star">收藏</string>
<string name="download.menu_clear_playlist">清空播放列表</string>
<string name="button_bar.shares">分享</string>
<string name="select_share.empty">服务器上没有可用的共享</string>
<string name="menu_deleted_share">删除分享 %s</string>
<string name="menu_deleted_share_error">删除分享失败 %s</string>
<string name="settings.share_milliseconds">毫秒</string>
<string name="settings.share_seconds"></string>
<string name="settings.share_minutes">分钟</string>
@ -251,27 +363,64 @@
<string name="settings.share_days"></string>
<string name="time_span_disable">禁用</string>
<string name="time_span_disabled">已禁用</string>
<string name="save_as_defaults">保存为默认</string>
<string name="share_comment">评论</string>
<string name="settings.share_expiration">有效期</string>
<string name="download_song_removed">%s已从播放列表中移除</string>
<string name="download.share_playlist">分享播放列表</string>
<string name="settings.share_greeting_default">默认分享问候语</string>
<string name="share_default_greeting">看看我从 %s 分享的这首音乐</string>
<string name="share_via">分享歌曲通过</string>
<string name="menu.share">分享</string>
<string name="select_album_all_songs">%s 的所有歌曲</string>
<string name="settings.show_all_songs_by_artist">按艺术家显示所有歌曲</string>
<string name="settings.show_all_songs_by_artist_summary">在艺术家视图中添加新条目以访问艺术家的所有歌曲</string>
<string name="download.menu_show_artist">显示艺术家</string>
<string name="albumArt">albumArt</string>
<string name="common_multiple_years">Multiple Years</string>
<string name="settings.playback.resume_on_bluetooth_device">连接蓝牙设备时恢复播放</string>
<string name="settings.playback.pause_on_bluetooth_device">断开蓝牙设备时暂停播放</string>
<string name="settings.playback.bluetooth_all">所有蓝牙设备</string>
<string name="settings.playback.bluetooth_a2dp">仅音频 (A2DP) 设备</string>
<string name="settings.playback.bluetooth_disabled">已禁用</string>
<string name="settings.playback.single_button_bluetooth_device">启用蓝牙设备上的单键播放/暂停</string>
<string name="settings.playback.single_button_bluetooth_device_summary">当播放/暂停无法正常工作时,启用此功能可能对较旧的蓝牙设备有所帮助</string>
<string name="settings.debug.title">调试选项</string>
<string name="settings.debug.log_to_file">将调试日志写入文件</string>
<string name="settings.debug.log_path">日志文件可在 %1$s/%2$s 获取</string>
<string name="settings.debug.log_summary">%3$s 目录中有 %1$s 个日志文件占用了 ~%2$s MB 空间。您想保留这些吗?</string>
<string name="settings.debug.log_keep">保留文件</string>
<string name="settings.debug.log_delete">删除文件</string>
<string name="settings.debug.log_deleted">删除日志文件</string>
<string name="notification.downloading_title">在后台下载媒体…</string>
<string name="permissions.access_error">Ultrasonic 无法访问音乐文件缓存,缓存位置已重置为默认路径。</string>
<string name="permissions.message_box_title">警告</string>
<string name="permissions.permission_missing">Ultrasonic 需要对音乐缓存目录的读/写权限,缓存位置已重置为默认路径。</string>
<string name="permissions.rationale_title">需要权限</string>
<string name="permissions.rationale_description_failed">Ultrasonic 需要对音乐缓存目录具有读/写权限。\n请允许 Ultrasonic 访问文件系统。</string>
<string name="permissions.permanent_denial_description">Ultrasonic 需要对音乐缓存目录具有读/写权限。您可以在应用程序设置中授予该权限,否则将以默认路径作为缓存目录。</string>
<string name="permissions.open_settings">打开设置</string>
<string name="permissions.rationale_description_initial">为了更改缓存位置Ultrasonic 需要对文件系统具有读/写权限。</string>
<string name="filepicker.select_folder">选择文件夹</string>
<string name="filepicker.create_folder">创建文件夹</string>
<string name="filepicker.create_folder_failed">无法创建文件夹</string>
<string name="filepicker.internal">%1$s (内置)</string>
<string name="filepicker.default_app_folder">默认应用文件夹 %1$s (外置)</string>
<string name="filepicker.enter_folder_name">输入文件夹名称</string>
<string name="filepicker.create">创建</string>
<string name="filepicker.name_invalid">请输入一个有效的文件夹名称</string>
<string name="filepicker.already_exists">该文件夹已存在。\n请为该文件夹提供另一个名称</string>
<string name="filepicker.select">选择</string>
<string name="filepicker.default">使用默认值</string>
<string name="filepicker.available_drives">可用驱动器:</string>
<string name="server_selector.label">配置服务器</string>
<string name="server_selector.delete_confirmation">您确定要删除此服务器吗?</string>
<string name="server_editor.label">编辑服务器</string>
<string name="server_editor.new_label">添加服务器</string>
<string name="server_editor.leave_confirmation">您确定要离开并丢弃您的更改吗?</string>
<string name="server_editor.required">此项必填</string>
<string name="server_menu.edit">编辑</string>
<string name="server_menu.delete">删除</string>
@ -279,11 +428,47 @@
<string name="server_menu.move_down">下移</string>
<string name="server_editor.authentication">认证</string>
<string name="server_editor.advanced">高级设置</string>
<string name="server_editor.disabled_feature">一项或多项功能被禁用,因为此服务器不支持它们。\您可以随时再次运行此测试。</string>
<string name="server_menu.demo">试用服务器</string>
<plurals name="select_album_n_songs">
<item quantity="other">%d 首曲目</item>
</plurals>
<plurals name="select_album_n_songs_pinned">
<item quantity="other">已选择 %d 首歌曲进行固定。</item>
</plurals>
<plurals name="select_album_n_songs_downloaded">
<item quantity="other">已选择要下载 %d 首歌曲。</item>
</plurals>
<plurals name="select_album_n_songs_unpinned">
<item quantity="other">已选择 %d 首歌曲取消固定。</item>
</plurals>
<plurals name="select_album_n_songs_added">
<item quantity="other">已将 %d 首歌曲添加到播放队列的末尾。</item>
</plurals>
<plurals name="select_album_n_songs_play_next">
<item quantity="other">在当前歌曲之后插入了 %d 首歌曲。</item>
</plurals>
<plurals name="select_album_donate_dialog_n_trial_days_left">
<item quantity="other">试用期还剩 %d 天</item>
</plurals>
<!-- Subsonic api errors -->
<string name="api.subsonic.generic">一般api错误: %1$s</string>
<string name="api.subsonic.generic.no.message">服务器未发送任何信息</string>
<string name="api.subsonic.token_auth_not_supported_for_ldap">LDAP用户不支持以token形式授权连接。</string>
<string name="api.subsonic.not_authenticated">用户名或密码错误</string>
<string name="api.subsonic.not_authorized">授权失败,请在 Subsonic server 检查用户权限。</string>
<string name="api.subsonic.param_missing">缺少必需的参数。</string>
<string name="api.subsonic.requested_data_was_not_found">未找到请求的数据。</string>
<string name="api.subsonic.trial_period_is_over">试用期结束</string>
<string name="api.subsonic.upgrade_client">版本不兼容,请升级 Ultrasonic 应用。</string>
<string name="api.subsonic.upgrade_server">不兼容的版本。请升级Subsonic 服务。</string>
</resources>
<!-- Subsonic feature flags -->
<string name="feature_flags_category_title">特性标志</string>
<string name="feature_flags_five_star_rating_title">为歌曲使用五星评分</string>
<string name="feature_flags_five_star_rating_description">对歌曲使用五星级评级系统
而不是简单地为项目加星标/取消星标。</string>
</resources>

View File

@ -28,7 +28,9 @@
<string name="button_bar.playlists">Playlists</string>
<string name="button_bar.search">Search</string>
<string name="chat.send_a_message">Send a message</string>
<string name="common.album">Album</string>
<string name="common.appname">Ultrasonic</string>
<string name="common.artist">Artist</string>
<string name="common.cancel">Cancel</string>
<string name="common.comment">Comment</string>
<string name="common.confirm">Confirm</string>
@ -48,6 +50,7 @@
<string name="common.play_shuffled">Play Shuffled</string>
<string name="common.public">Public</string>
<string name="common.save">Save</string>
<string name="common.title">Title</string>
<string name="common.unpin">Unpin</string>
<string name="common.various_artists">Various Artists</string>
<string name="delete_playlist">Do you want to delete %1$s</string>
@ -350,11 +353,13 @@
<string name="widget.sdcard_missing">No SD card</string>
<string name="settings.share_description_default">Default Share Description</string>
<string name="settings.sharing_title">Sharing</string>
<string name="settings.sharing_always_ask_for_details_summary">Always ask for description and expiration when creating a share</string>
<string name="settings.sharing_always_ask_for_details_summary">Always ask for description and expiration when creating a share on the server</string>
<string name="settings.sharing_always_ask_for_details">Always Ask For Details</string>
<string name="settings.share_expiration_default">Default Expiration Time</string>
<string name="do_not_show_dialog_again">Do not show dialog again</string>
<string name="share_set_share_options">Set Share Options</string>
<string name="share_on_server">Create share on the server</string>
<string name="settings.share_on_server_summary">Sharing will create a share on the server and share its URL. If disabled, only the song details are shared</string>
<string name="no_expiration">No Expiration</string>
<string name="download.toggle_playlist">Toggle Playlist</string>
<string name="download.bookmark_set">Set Bookmark</string>
@ -377,6 +382,7 @@
<string name="settings.share_expiration">Time To Expiration</string>
<string name="download_song_removed">\"%s\" was removed from playlist</string>
<string name="download.share_playlist">Share Playlist</string>
<string name="download.share_song">Share Current Song</string>
<string name="settings.share_greeting_default">Default Share Greeting</string>
<string name="share_default_greeting">Check out this music I shared from %s</string>
<string name="share_via">Share songs via</string>

View File

@ -15,7 +15,7 @@
<item name="android:textSize">14sp</item>
<item name="android:textAllCaps">true</item>
<item name="android:gravity">center_vertical</item>
<item name="android:paddingLeft">16dp</item>
<item name="android:paddingStart">16dp</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item>
</style>

View File

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:a="http://schemas.android.com/apk/res/android"
a:minWidth="250dp"
a:minHeight="40dp"
a:updatePeriodMillis="0"
a:resizeMode="none"
a:previewImage="@drawable/preview4x1"
a:widgetCategory="home_screen|keyguard"
a:initialLayout="@layout/appwidget4x1"/>
xmlns:tools="http://schemas.android.com/tools"
a:minWidth="250dp"
a:minHeight="40dp"
a:updatePeriodMillis="0"
a:resizeMode="none"
a:previewImage="@drawable/preview4x1"
a:widgetCategory="home_screen|keyguard"
a:initialLayout="@layout/appwidget4x1"
tools:ignore="UnusedAttribute" />

View File

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:a="http://schemas.android.com/apk/res/android"
a:minWidth="250dp"
a:minHeight="110dp"
a:updatePeriodMillis="0"
a:resizeMode="none"
a:previewImage="@drawable/preview4x2"
a:widgetCategory="home_screen|keyguard"
a:initialLayout="@layout/appwidget4x2"/>
xmlns:tools="http://schemas.android.com/tools"
a:minWidth="250dp"
a:minHeight="110dp"
a:updatePeriodMillis="0"
a:resizeMode="none"
a:previewImage="@drawable/preview4x2"
a:widgetCategory="home_screen|keyguard"
a:initialLayout="@layout/appwidget4x2"
tools:ignore="UnusedAttribute" />

View File

@ -180,18 +180,15 @@
<PreferenceCategory
a:title="@string/settings.sharing_title"
app:iconSpaceReserved="false">
<EditTextPreference
a:key="sharingDefaultDescription"
a:title="@string/settings.share_description_default"
app:iconSpaceReserved="false"/>
<EditTextPreference
a:key="sharingDefaultGreeting"
a:title="@string/settings.share_greeting_default"
app:iconSpaceReserved="false"/>
<org.moire.ultrasonic.util.TimeSpanPreference
a:defaultValue="0"
a:key="sharingDefaultExpiration"
a:title="@string/settings.share_expiration_default"
<CheckBoxPreference
a:defaultValue="true"
a:key="sharingCreateOnServer"
a:title="@string/share_on_server"
a:summary="@string/settings.share_on_server_summary"
app:iconSpaceReserved="false"/>
<CheckBoxPreference
a:defaultValue="true"
@ -199,6 +196,15 @@
a:summary="@string/settings.sharing_always_ask_for_details_summary"
a:title="@string/settings.sharing_always_ask_for_details"
app:iconSpaceReserved="false"/>
<EditTextPreference
a:key="sharingDefaultDescription"
a:title="@string/settings.share_description_default"
app:iconSpaceReserved="false"/>
<org.moire.ultrasonic.util.TimeSpanPreference
a:defaultValue="0"
a:key="sharingDefaultExpiration"
a:title="@string/settings.share_expiration_default"
app:iconSpaceReserved="false"/>
</PreferenceCategory>
<PreferenceCategory
a:title="@string/settings.network_title"