parent
fa4214a0ac
commit
66e7732ec2
|
@ -76,7 +76,7 @@ style:
|
||||||
UnnecessaryAbstractClass:
|
UnnecessaryAbstractClass:
|
||||||
active: false
|
active: false
|
||||||
ReturnCount:
|
ReturnCount:
|
||||||
max: 3
|
max: 5
|
||||||
|
|
||||||
comments:
|
comments:
|
||||||
active: true
|
active: true
|
||||||
|
|
|
@ -20,6 +20,7 @@ import androidx.preference.ListPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceCategory
|
import androidx.preference.PreferenceCategory
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import java.io.File
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.java.KoinJavaComponent.get
|
import org.koin.java.KoinJavaComponent.get
|
||||||
|
@ -52,7 +53,6 @@ import org.moire.ultrasonic.util.TimeSpanPreferenceDialogFragmentCompat
|
||||||
import org.moire.ultrasonic.util.Util.toast
|
import org.moire.ultrasonic.util.Util.toast
|
||||||
import org.moire.ultrasonic.util.isUri
|
import org.moire.ultrasonic.util.isUri
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows main app settings.
|
* Shows main app settings.
|
||||||
|
|
|
@ -3,11 +3,11 @@ package org.moire.ultrasonic.imageloader
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import java.io.File
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory
|
import org.moire.ultrasonic.domain.MusicDirectory
|
||||||
import org.moire.ultrasonic.util.FileUtil
|
import org.moire.ultrasonic.util.FileUtil
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.Util
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
@Suppress("UtilityClassWithPublicConstructor")
|
@Suppress("UtilityClassWithPublicConstructor")
|
||||||
class BitmapUtils {
|
class BitmapUtils {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import androidx.core.content.ContextCompat
|
||||||
import com.squareup.picasso.LruCache
|
import com.squareup.picasso.LruCache
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import com.squareup.picasso.RequestCreator
|
import com.squareup.picasso.RequestCreator
|
||||||
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
@ -22,7 +23,6 @@ import org.moire.ultrasonic.domain.MusicDirectory
|
||||||
import org.moire.ultrasonic.util.FileUtil
|
import org.moire.ultrasonic.util.FileUtil
|
||||||
import org.moire.ultrasonic.util.Util.safeClose
|
import org.moire.ultrasonic.util.Util.safeClose
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our new image loader which uses Picasso as a backend.
|
* Our new image loader which uses Picasso as a backend.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.moire.ultrasonic.log
|
package org.moire.ultrasonic.log
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
import java.io.FileWriter
|
import java.io.FileWriter
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
@ -7,7 +8,6 @@ import java.util.Locale
|
||||||
import org.moire.ultrasonic.util.FileUtil
|
import org.moire.ultrasonic.util.FileUtil
|
||||||
import org.moire.ultrasonic.util.Util.safeClose
|
import org.moire.ultrasonic.util.Util.safeClose
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Timber Tree which can be used to log to a file
|
* A Timber Tree which can be used to log to a file
|
||||||
|
|
|
@ -22,9 +22,9 @@ import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
|
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
|
||||||
import org.moire.ultrasonic.util.CacheCleaner
|
import org.moire.ultrasonic.util.CacheCleaner
|
||||||
import org.moire.ultrasonic.util.CancellableTask
|
import org.moire.ultrasonic.util.CancellableTask
|
||||||
import org.moire.ultrasonic.util.Storage
|
|
||||||
import org.moire.ultrasonic.util.FileUtil
|
import org.moire.ultrasonic.util.FileUtil
|
||||||
import org.moire.ultrasonic.util.Settings
|
import org.moire.ultrasonic.util.Settings
|
||||||
|
import org.moire.ultrasonic.util.Storage
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.Util
|
||||||
import org.moire.ultrasonic.util.Util.safeClose
|
import org.moire.ultrasonic.util.Util.safeClose
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -73,8 +73,10 @@ class DownloadFile(
|
||||||
init {
|
init {
|
||||||
val state: DownloadStatus
|
val state: DownloadStatus
|
||||||
|
|
||||||
partialFile = FileUtil.getParentPath(saveFile) + "/" + FileUtil.getPartialFile(FileUtil.getNameFromPath(saveFile))
|
partialFile = FileUtil.getParentPath(saveFile) + "/" +
|
||||||
completeFile = FileUtil.getParentPath(saveFile) + "/" + FileUtil.getCompleteFile(FileUtil.getNameFromPath(saveFile))
|
FileUtil.getPartialFile(FileUtil.getNameFromPath(saveFile))
|
||||||
|
completeFile = FileUtil.getParentPath(saveFile) + "/" +
|
||||||
|
FileUtil.getCompleteFile(FileUtil.getNameFromPath(saveFile))
|
||||||
|
|
||||||
when {
|
when {
|
||||||
Storage.isPathExists(saveFile) -> {
|
Storage.isPathExists(saveFile) -> {
|
||||||
|
@ -146,11 +148,11 @@ class DownloadFile(
|
||||||
|
|
||||||
@get:Synchronized
|
@get:Synchronized
|
||||||
val isDownloading: Boolean
|
val isDownloading: Boolean
|
||||||
get() = downloadPrepared || (downloadTask != null && downloadTask!!.isRunning)
|
get() = downloadPrepared || (downloadTask != null && downloadTask!!.isRunning)
|
||||||
|
|
||||||
@get:Synchronized
|
@get:Synchronized
|
||||||
val isDownloadCancelled: Boolean
|
val isDownloadCancelled: Boolean
|
||||||
get() = downloadTask != null && downloadTask!!.isCancelled
|
get() = downloadTask != null && downloadTask!!.isCancelled
|
||||||
|
|
||||||
fun shouldRetry(): Boolean {
|
fun shouldRetry(): Boolean {
|
||||||
return (retryCount > 0)
|
return (retryCount > 0)
|
||||||
|
@ -254,9 +256,8 @@ class DownloadFile(
|
||||||
val fileLength = Storage.getFromPath(partialFile)?.length ?: 0
|
val fileLength = Storage.getFromPath(partialFile)?.length ?: 0
|
||||||
|
|
||||||
needsDownloading = (
|
needsDownloading = (
|
||||||
desiredBitRate == 0 || duration == null ||
|
desiredBitRate == 0 || duration == null || duration == 0 || fileLength == 0L
|
||||||
duration == 0 || fileLength == 0L
|
)
|
||||||
)
|
|
||||||
|
|
||||||
if (needsDownloading) {
|
if (needsDownloading) {
|
||||||
// Attempt partial HTTP GET, appending to the file if it exists.
|
// Attempt partial HTTP GET, appending to the file if it exists.
|
||||||
|
@ -270,7 +271,8 @@ class DownloadFile(
|
||||||
Timber.i("Executed partial HTTP GET, skipping %d bytes", fileLength)
|
Timber.i("Executed partial HTTP GET, skipping %d bytes", fileLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
outputStream = Storage.getOrCreateFileFromPath(partialFile).getFileOutputStream(isPartial)
|
outputStream = Storage.getOrCreateFileFromPath(partialFile)
|
||||||
|
.getFileOutputStream(isPartial)
|
||||||
|
|
||||||
val len = inputStream.copyTo(outputStream) { totalBytesCopied ->
|
val len = inputStream.copyTo(outputStream) { totalBytesCopied ->
|
||||||
setProgress(totalBytesCopied)
|
setProgress(totalBytesCopied)
|
||||||
|
@ -395,7 +397,7 @@ class DownloadFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
override val id: String
|
override val id: String
|
||||||
get() = song.id
|
get() = song.id
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val MAX_RETRIES = 5
|
const val MAX_RETRIES = 5
|
||||||
|
|
|
@ -187,7 +187,6 @@ class Downloader(
|
||||||
if (listChanged) {
|
if (listChanged) {
|
||||||
updateLiveData()
|
updateLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateLiveData() {
|
private fun updateLiveData() {
|
||||||
|
|
|
@ -19,7 +19,6 @@ import android.os.PowerManager
|
||||||
import android.os.PowerManager.PARTIAL_WAKE_LOCK
|
import android.os.PowerManager.PARTIAL_WAKE_LOCK
|
||||||
import android.os.PowerManager.WakeLock
|
import android.os.PowerManager.WakeLock
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import org.moire.ultrasonic.util.Storage
|
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
@ -33,6 +32,7 @@ import org.moire.ultrasonic.domain.PlayerState
|
||||||
import org.moire.ultrasonic.util.CancellableTask
|
import org.moire.ultrasonic.util.CancellableTask
|
||||||
import org.moire.ultrasonic.util.Constants
|
import org.moire.ultrasonic.util.Constants
|
||||||
import org.moire.ultrasonic.util.Settings
|
import org.moire.ultrasonic.util.Settings
|
||||||
|
import org.moire.ultrasonic.util.Storage
|
||||||
import org.moire.ultrasonic.util.StreamProxy
|
import org.moire.ultrasonic.util.StreamProxy
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.Util
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
|
@ -9,7 +9,9 @@ package org.moire.ultrasonic.service
|
||||||
import android.media.MediaMetadataRetriever
|
import android.media.MediaMetadataRetriever
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.BufferedWriter
|
import java.io.BufferedWriter
|
||||||
import org.moire.ultrasonic.util.Storage
|
import java.io.File
|
||||||
|
import java.io.FileReader
|
||||||
|
import java.io.FileWriter
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.Reader
|
import java.io.Reader
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
|
@ -40,11 +42,9 @@ import org.moire.ultrasonic.domain.UserInfo
|
||||||
import org.moire.ultrasonic.util.AbstractFile
|
import org.moire.ultrasonic.util.AbstractFile
|
||||||
import org.moire.ultrasonic.util.Constants
|
import org.moire.ultrasonic.util.Constants
|
||||||
import org.moire.ultrasonic.util.FileUtil
|
import org.moire.ultrasonic.util.FileUtil
|
||||||
|
import org.moire.ultrasonic.util.Storage
|
||||||
import org.moire.ultrasonic.util.Util.safeClose
|
import org.moire.ultrasonic.util.Util.safeClose
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
|
||||||
import java.io.FileReader
|
|
||||||
import java.io.FileWriter
|
|
||||||
|
|
||||||
@Suppress("TooManyFunctions")
|
@Suppress("TooManyFunctions")
|
||||||
class OfflineMusicService : MusicService, KoinComponent {
|
class OfflineMusicService : MusicService, KoinComponent {
|
||||||
|
@ -502,7 +502,6 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||||
return FileUtil.getBaseName(name)
|
return FileUtil.getBaseName(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun createEntry(file: AbstractFile, name: String?): MusicDirectory.Entry {
|
private fun createEntry(file: AbstractFile, name: String?): MusicDirectory.Entry {
|
||||||
val entry = MusicDirectory.Entry(file.path)
|
val entry = MusicDirectory.Entry(file.path)
|
||||||
entry.populateWithDataFrom(file, name)
|
entry.populateWithDataFrom(file, name)
|
||||||
|
|
|
@ -12,7 +12,7 @@ import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
abstract class AbstractFile: Comparable<AbstractFile> {
|
abstract class AbstractFile : Comparable<AbstractFile> {
|
||||||
abstract val name: String
|
abstract val name: String
|
||||||
abstract val isDirectory: Boolean
|
abstract val isDirectory: Boolean
|
||||||
abstract val isFile: Boolean
|
abstract val isFile: Boolean
|
||||||
|
@ -53,4 +53,4 @@ abstract class AbstractFile: Comparable<AbstractFile> {
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun rename(pathFrom: AbstractFile, pathTo: String)
|
abstract fun rename(pathFrom: AbstractFile, pathTo: String)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,13 @@ import org.koin.java.KoinJavaComponent.inject
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||||
import org.moire.ultrasonic.domain.Playlist
|
import org.moire.ultrasonic.domain.Playlist
|
||||||
import org.moire.ultrasonic.service.Downloader
|
import org.moire.ultrasonic.service.Downloader
|
||||||
|
import org.moire.ultrasonic.util.FileUtil.delete
|
||||||
import org.moire.ultrasonic.util.FileUtil.getAlbumArtFile
|
import org.moire.ultrasonic.util.FileUtil.getAlbumArtFile
|
||||||
import org.moire.ultrasonic.util.FileUtil.getPlaylistDirectory
|
import org.moire.ultrasonic.util.FileUtil.getPlaylistDirectory
|
||||||
import org.moire.ultrasonic.util.FileUtil.getPlaylistFile
|
import org.moire.ultrasonic.util.FileUtil.getPlaylistFile
|
||||||
import org.moire.ultrasonic.util.FileUtil.listFiles
|
import org.moire.ultrasonic.util.FileUtil.listFiles
|
||||||
import org.moire.ultrasonic.util.FileUtil.musicDirectory
|
import org.moire.ultrasonic.util.FileUtil.musicDirectory
|
||||||
import org.moire.ultrasonic.util.Settings.cacheSizeMB
|
import org.moire.ultrasonic.util.Settings.cacheSizeMB
|
||||||
import org.moire.ultrasonic.util.FileUtil.delete
|
|
||||||
import org.moire.ultrasonic.util.Util.formatBytes
|
import org.moire.ultrasonic.util.Util.formatBytes
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import android.os.Environment
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.util.Pair
|
import android.util.Pair
|
||||||
import java.io.BufferedWriter
|
import java.io.BufferedWriter
|
||||||
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.FileWriter
|
import java.io.FileWriter
|
||||||
|
@ -28,7 +29,6 @@ import org.moire.ultrasonic.app.UApp
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory
|
import org.moire.ultrasonic.domain.MusicDirectory
|
||||||
import org.moire.ultrasonic.util.Util.safeClose
|
import org.moire.ultrasonic.util.Util.safeClose
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
@Suppress("TooManyFunctions")
|
@Suppress("TooManyFunctions")
|
||||||
object FileUtil {
|
object FileUtil {
|
||||||
|
@ -191,11 +191,11 @@ object FileUtil {
|
||||||
if (!TextUtils.isEmpty(entry.path) && getParentPath(entry.path!!) != null) {
|
if (!TextUtils.isEmpty(entry.path) && getParentPath(entry.path!!) != null) {
|
||||||
val f = fileSystemSafeDir(entry.path)
|
val f = fileSystemSafeDir(entry.path)
|
||||||
dir = String.format(
|
dir = String.format(
|
||||||
Locale.ROOT,
|
Locale.ROOT,
|
||||||
"%s/%s",
|
"%s/%s",
|
||||||
musicDirectory.path,
|
musicDirectory.path,
|
||||||
if (entry.isDirectory) f else getParentPath(f) ?: ""
|
if (entry.isDirectory) f else getParentPath(f) ?: ""
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
val artist = fileSystemSafe(entry.artist)
|
val artist = fileSystemSafe(entry.artist)
|
||||||
var album = fileSystemSafe(entry.album)
|
var album = fileSystemSafe(entry.album)
|
||||||
|
|
|
@ -9,14 +9,14 @@ package org.moire.ultrasonic.util
|
||||||
|
|
||||||
import android.content.res.AssetFileDescriptor
|
import android.content.res.AssetFileDescriptor
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import org.moire.ultrasonic.app.UApp
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
import org.moire.ultrasonic.app.UApp
|
||||||
|
|
||||||
class JavaFile(override val parent: AbstractFile?, val file: File): AbstractFile() {
|
class JavaFile(override val parent: AbstractFile?, val file: File) : AbstractFile() {
|
||||||
override val name: String = file.name
|
override val name: String = file.name
|
||||||
override val isDirectory: Boolean = file.isDirectory
|
override val isDirectory: Boolean = file.isDirectory
|
||||||
override val isFile: Boolean = file.isFile
|
override val isFile: Boolean = file.isFile
|
||||||
|
@ -33,7 +33,7 @@ class JavaFile(override val parent: AbstractFile?, val file: File): AbstractFile
|
||||||
|
|
||||||
override fun listFiles(): Array<AbstractFile> {
|
override fun listFiles(): Array<AbstractFile> {
|
||||||
val fileList = file.listFiles()
|
val fileList = file.listFiles()
|
||||||
return fileList?.map { file -> JavaFile(this, file) }?.toTypedArray() ?: emptyArray()
|
return fileList?.map { file -> JavaFile(this, file) }?.toTypedArray() ?: emptyArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFileOutputStream(append: Boolean): OutputStream {
|
override fun getFileOutputStream(append: Boolean): OutputStream {
|
||||||
|
|
|
@ -22,4 +22,4 @@ class ResettableLazy<T>(private val initializer: () -> T) {
|
||||||
fun reset() {
|
fun reset() {
|
||||||
lazyRef.set(lazy(initializer))
|
lazyRef.set(lazy(initializer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ package org.moire.ultrasonic.util
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import org.moire.ultrasonic.R
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.app.UApp
|
import org.moire.ultrasonic.app.UApp
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import timber.log.Timber
|
||||||
object Storage {
|
object Storage {
|
||||||
|
|
||||||
val mediaRoot: ResettableLazy<AbstractFile> = ResettableLazy {
|
val mediaRoot: ResettableLazy<AbstractFile> = ResettableLazy {
|
||||||
getRoot()!!
|
getRoot()!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRoot(): AbstractFile? {
|
private fun getRoot(): AbstractFile? {
|
||||||
|
|
|
@ -1,32 +1,54 @@
|
||||||
|
/*
|
||||||
|
* StorageFile.kt
|
||||||
|
* Copyright (C) 2009-2021 Ultrasonic developers
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GNU GPLv3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
package org.moire.ultrasonic.util
|
package org.moire.ultrasonic.util
|
||||||
|
|
||||||
import android.content.res.AssetFileDescriptor
|
import android.content.res.AssetFileDescriptor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
import androidx.documentfile.provider.DocumentFile
|
|
||||||
import org.moire.ultrasonic.app.UApp
|
|
||||||
import timber.log.Timber
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import org.moire.ultrasonic.app.UApp
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
class StorageFile(
|
class StorageFile(
|
||||||
override val parent: StorageFile?,
|
override val parent: StorageFile?,
|
||||||
var uri: Uri,
|
var uri: Uri,
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val isDirectory: Boolean
|
override val isDirectory: Boolean
|
||||||
): AbstractFile() {
|
) : AbstractFile() {
|
||||||
private val documentFile: DocumentFile = DocumentFile.fromSingleUri(UApp.applicationContext(), uri)!!
|
|
||||||
|
|
||||||
override val isFile: Boolean = !isDirectory
|
override val isFile: Boolean = !isDirectory
|
||||||
|
|
||||||
override val length: Long
|
override val length: Long
|
||||||
get() = documentFile.length()
|
get() {
|
||||||
|
val resolver = UApp.applicationContext().contentResolver
|
||||||
|
val column = arrayOf(DocumentsContract.Document.COLUMN_SIZE)
|
||||||
|
resolver.query(uri, column, null, null, null)?.use { cursor ->
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
return cursor.getLong(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
override val lastModified: Long
|
override val lastModified: Long
|
||||||
get() = documentFile.lastModified()
|
get() {
|
||||||
|
val resolver = UApp.applicationContext().contentResolver
|
||||||
|
val column = arrayOf(DocumentsContract.Document.COLUMN_LAST_MODIFIED)
|
||||||
|
resolver.query(uri, column, null, null, null)?.use { cursor ->
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
return cursor.getLong(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
override val path: String
|
override val path: String
|
||||||
get() {
|
get() {
|
||||||
|
@ -94,7 +116,6 @@ class StorageFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFromPath(path: String): StorageFile? {
|
override fun getFromPath(path: String): StorageFile? {
|
||||||
|
|
||||||
if (storageFilePathDictionary.containsKey(path))
|
if (storageFilePathDictionary.containsKey(path))
|
||||||
return storageFilePathDictionary[path]!!
|
return storageFilePathDictionary[path]!!
|
||||||
if (notExistingPathDictionary.contains(path)) return null
|
if (notExistingPathDictionary.contains(path)) return null
|
||||||
|
@ -108,7 +129,7 @@ class StorageFile(
|
||||||
val fileName = FileUtil.getNameFromPath(path)
|
val fileName = FileUtil.getNameFromPath(path)
|
||||||
var file: StorageFile? = null
|
var file: StorageFile? = null
|
||||||
|
|
||||||
Timber.v("StorageFile getFromPath path: $path")
|
Timber.v("StorageFile getFromPath listing files on path: $path")
|
||||||
parent.listFiles().forEach {
|
parent.listFiles().forEach {
|
||||||
if (it.name == fileName) file = it as StorageFile
|
if (it.name == fileName) file = it as StorageFile
|
||||||
storageFilePathDictionary[it.path] = it as StorageFile
|
storageFilePathDictionary[it.path] = it as StorageFile
|
||||||
|
@ -149,18 +170,28 @@ class StorageFile(
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
override fun rename(pathFrom: AbstractFile, pathTo: String) {
|
override fun rename(pathFrom: AbstractFile, pathTo: String) {
|
||||||
val storagePathFrom = pathFrom as StorageFile
|
val fileFrom = pathFrom as StorageFile
|
||||||
if (!storagePathFrom.documentFile.exists()) throw IOException("File to rename doesn't exist")
|
if (!fileFrom.exists()) throw IOException("File to rename doesn't exist")
|
||||||
Timber.d("Renaming from %s to %s", storagePathFrom.path, pathTo)
|
Timber.d("Renaming from %s to %s", fileFrom.path, pathTo)
|
||||||
|
|
||||||
val parentTo = getFromPath(FileUtil.getParentPath(pathTo)!!) ?: throw IOException("Destination folder doesn't exist")
|
val parentTo = getFromPath(FileUtil.getParentPath(pathTo)!!)
|
||||||
|
?: throw IOException("Destination folder doesn't exist")
|
||||||
val fileTo = getFromParentAndName(parentTo, FileUtil.getNameFromPath(pathTo))
|
val fileTo = getFromParentAndName(parentTo, FileUtil.getNameFromPath(pathTo))
|
||||||
|
|
||||||
copyFileContents(storagePathFrom.documentFile, fileTo.documentFile)
|
copyFileContents(fileFrom, fileTo)
|
||||||
storagePathFrom.delete()
|
fileFrom.delete()
|
||||||
|
|
||||||
notExistingPathDictionary.remove(pathTo)
|
notExistingPathDictionary.remove(pathTo)
|
||||||
storageFilePathDictionary.remove(storagePathFrom.path)
|
storageFilePathDictionary.remove(fileFrom.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun exists(): Boolean {
|
||||||
|
val resolver = UApp.applicationContext().contentResolver
|
||||||
|
val column = arrayOf(DocumentsContract.Document.COLUMN_DISPLAY_NAME)
|
||||||
|
resolver.query(uri, column, null, null, null)?.use { cursor ->
|
||||||
|
if (cursor.count != 0) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getChildren(): List<StorageFile> {
|
private fun getChildren(): List<StorageFile> {
|
||||||
|
@ -209,9 +240,9 @@ class StorageFile(
|
||||||
DocumentsContract.Document.COLUMN_MIME_TYPE
|
DocumentsContract.Document.COLUMN_MIME_TYPE
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun copyFileContents(sourceFile: DocumentFile, destinationFile: DocumentFile) {
|
private fun copyFileContents(sourceFile: AbstractFile, destinationFile: AbstractFile) {
|
||||||
UApp.applicationContext().contentResolver.openInputStream(sourceFile.uri)?.use { inputStream ->
|
sourceFile.getFileInputStream().use { inputStream ->
|
||||||
UApp.applicationContext().contentResolver.openOutputStream(destinationFile.uri)?.use { outputStream ->
|
destinationFile.getFileOutputStream(false).use { outputStream ->
|
||||||
inputStream.copyInto(outputStream)
|
inputStream.copyInto(outputStream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,9 +269,7 @@ class StorageFile(
|
||||||
return storageFilePathDictionary[parentPath]!!
|
return storageFilePathDictionary[parentPath]!!
|
||||||
if (notExistingPathDictionary.contains(parentPath)) return null
|
if (notExistingPathDictionary.contains(parentPath)) return null
|
||||||
|
|
||||||
val start = System.currentTimeMillis()
|
|
||||||
val parent = findStorageFileForParentDirectory(parentPath)
|
val parent = findStorageFileForParentDirectory(parentPath)
|
||||||
val end = System.currentTimeMillis()
|
|
||||||
|
|
||||||
if (parent == null) {
|
if (parent == null) {
|
||||||
storageFilePathDictionary.remove(parentPath)
|
storageFilePathDictionary.remove(parentPath)
|
||||||
|
@ -253,6 +282,7 @@ class StorageFile(
|
||||||
return parent
|
return parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("NestedBlockDepth")
|
||||||
private fun findStorageFileForParentDirectory(path: String): StorageFile? {
|
private fun findStorageFileForParentDirectory(path: String): StorageFile? {
|
||||||
val segments = getUriSegments(path)
|
val segments = getUriSegments(path)
|
||||||
?: throw IOException("Can't get path because the root has changed")
|
?: throw IOException("Can't get path because the root has changed")
|
||||||
|
|
|
@ -2,10 +2,10 @@ package org.moire.ultrasonic.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import java.io.File
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
import org.moire.ultrasonic.util.Util.safeClose
|
import org.moire.ultrasonic.util.Util.safeClose
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs the stack trace of uncaught exceptions to a file on the SD card.
|
* Logs the stack trace of uncaught exceptions to a file on the SD card.
|
||||||
|
|
|
@ -52,7 +52,6 @@ import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.app.UApp
|
|
||||||
import org.moire.ultrasonic.app.UApp.Companion.applicationContext
|
import org.moire.ultrasonic.app.UApp.Companion.applicationContext
|
||||||
import org.moire.ultrasonic.domain.Bookmark
|
import org.moire.ultrasonic.domain.Bookmark
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory
|
import org.moire.ultrasonic.domain.MusicDirectory
|
||||||
|
@ -757,7 +756,8 @@ object Util {
|
||||||
// TODO this doesn't work for URIs
|
// TODO this doesn't work for URIs
|
||||||
MediaScannerConnection.scanFile(
|
MediaScannerConnection.scanFile(
|
||||||
applicationContext(), arrayOf(file),
|
applicationContext(), arrayOf(file),
|
||||||
null, null)
|
null, null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getResourceFromAttribute(context: Context, resId: Int): Int {
|
fun getResourceFromAttribute(context: Context, resId: Int): Int {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
a:layout_width="wrap_content"
|
a:layout_width="wrap_content"
|
||||||
a:layout_height="wrap_content"
|
a:layout_height="wrap_content"
|
||||||
a:layout_gravity="center_vertical"
|
a:layout_gravity="center_vertical"
|
||||||
|
a:contentDescription="@null"
|
||||||
a:src="?attr/select_folder" />
|
a:src="?attr/select_folder" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|
|
@ -348,7 +348,6 @@
|
||||||
<string name="share_default_greeting">Mrkni na hudbu sdílenou z %s</string>
|
<string name="share_default_greeting">Mrkni na hudbu sdílenou z %s</string>
|
||||||
<string name="share_via">Sdílet skladby přes</string>
|
<string name="share_via">Sdílet skladby přes</string>
|
||||||
<string name="menu.share">Sdílení</string>
|
<string name="menu.share">Sdílení</string>
|
||||||
<string name="select_album_all_songs">Všechny skladby od %s</string>
|
|
||||||
<string name="settings.show_all_songs_by_artist">Zobrazit všechny skladby umělce</string>
|
<string name="settings.show_all_songs_by_artist">Zobrazit všechny skladby umělce</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Přidat nový zápis v náhledu umělců pro přístup ke všem skladbám umělce</string>
|
<string name="settings.show_all_songs_by_artist_summary">Přidat nový zápis v náhledu umělců pro přístup ke všem skladbám umělce</string>
|
||||||
<string name="download.menu_show_artist">Zobrazit umělce</string>
|
<string name="download.menu_show_artist">Zobrazit umělce</string>
|
||||||
|
|
|
@ -343,7 +343,6 @@
|
||||||
<string name="share_default_greeting">Hör dir mal die Musik an, die ich mit dir über %s geteilt habe.</string>
|
<string name="share_default_greeting">Hör dir mal die Musik an, die ich mit dir über %s geteilt habe.</string>
|
||||||
<string name="share_via">Titel teilen über</string>
|
<string name="share_via">Titel teilen über</string>
|
||||||
<string name="menu.share">Freigabe</string>
|
<string name="menu.share">Freigabe</string>
|
||||||
<string name="select_album_all_songs">Alle Titel von %s</string>
|
|
||||||
<string name="settings.show_all_songs_by_artist">Alle Titel nach Künstler sortieren</string>
|
<string name="settings.show_all_songs_by_artist">Alle Titel nach Künstler sortieren</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Einen neuen Eintrag in der Künstleransicht hinzufügen, um auf alle Lieder eines Künstlers zuzugreifen</string>
|
<string name="settings.show_all_songs_by_artist_summary">Einen neuen Eintrag in der Künstleransicht hinzufügen, um auf alle Lieder eines Künstlers zuzugreifen</string>
|
||||||
<string name="download.menu_show_artist">Künstler zeigen</string>
|
<string name="download.menu_show_artist">Künstler zeigen</string>
|
||||||
|
|
|
@ -371,7 +371,6 @@
|
||||||
<string name="share_default_greeting">Echa un vistazo a esta música que te comparto desde %s</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>
|
<string name="share_via">Compartir canciones vía</string>
|
||||||
<string name="menu.share">Compartir</string>
|
<string name="menu.share">Compartir</string>
|
||||||
<string name="select_album_all_songs">Todas las canciones por %s</string>
|
|
||||||
<string name="settings.show_all_songs_by_artist">Mostrar todas las canciones por artista</string>
|
<string name="settings.show_all_songs_by_artist">Mostrar todas las canciones por artista</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Añadir nueva entrada en la vista de artista para acceder a todas las canciones de un artista</string>
|
<string name="settings.show_all_songs_by_artist_summary">Añadir nueva entrada en la vista de artista para acceder a todas las canciones de un artista</string>
|
||||||
<string name="download.menu_show_artist">Mostrar artista</string>
|
<string name="download.menu_show_artist">Mostrar artista</string>
|
||||||
|
|
|
@ -362,7 +362,6 @@
|
||||||
<string name="share_default_greeting">Regardez cette musique que j\'ai partagée depuis %s</string>
|
<string name="share_default_greeting">Regardez cette musique que j\'ai partagée depuis %s</string>
|
||||||
<string name="share_via">Partager des titres via</string>
|
<string name="share_via">Partager des titres via</string>
|
||||||
<string name="menu.share">Partager</string>
|
<string name="menu.share">Partager</string>
|
||||||
<string name="select_album_all_songs">Tous les titres de %s</string>
|
|
||||||
<string name="settings.show_all_songs_by_artist">Voir tous les titres par artiste</string>
|
<string name="settings.show_all_songs_by_artist">Voir tous les titres par artiste</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Ajouter une nouvelle entrée dans la vue artiste pour accéder à toutes les titres d\'un artiste</string>
|
<string name="settings.show_all_songs_by_artist_summary">Ajouter une nouvelle entrée dans la vue artiste pour accéder à toutes les titres d\'un artiste</string>
|
||||||
<string name="download.menu_show_artist">Afficher l\'artiste</string>
|
<string name="download.menu_show_artist">Afficher l\'artiste</string>
|
||||||
|
|
|
@ -360,7 +360,6 @@
|
||||||
<string name="share_default_greeting">Hallgasd meg ezt a zenét, megosztottam innen: %s</string>
|
<string name="share_default_greeting">Hallgasd meg ezt a zenét, megosztottam innen: %s</string>
|
||||||
<string name="share_via">Dalok megosztása ezzel</string>
|
<string name="share_via">Dalok megosztása ezzel</string>
|
||||||
<string name="menu.share">Megosztás</string>
|
<string name="menu.share">Megosztás</string>
|
||||||
<string name="select_album_all_songs">%s minden dala</string>
|
|
||||||
<string name="settings.show_all_songs_by_artist">Az előadó összes dalának megjelenítése</string>
|
<string name="settings.show_all_songs_by_artist">Az előadó összes dalának megjelenítése</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Új bejegyzés hozzáadása az előadóhoz, az előadó összes dalának eléréséhez.</string>
|
<string name="settings.show_all_songs_by_artist_summary">Új bejegyzés hozzáadása az előadóhoz, az előadó összes dalának eléréséhez.</string>
|
||||||
<string name="download.menu_show_artist">Ugrás az előadóhoz</string>
|
<string name="download.menu_show_artist">Ugrás az előadóhoz</string>
|
||||||
|
|
|
@ -371,7 +371,6 @@
|
||||||
<string name="share_default_greeting">Hé, luister eens naar de muziek die ik heb gedeeld via %s</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>
|
<string name="share_via">Nummers delen via</string>
|
||||||
<string name="menu.share">Delen</string>
|
<string name="menu.share">Delen</string>
|
||||||
<string name="select_album_all_songs">Alle nummers van %s</string>
|
|
||||||
<string name="settings.show_all_songs_by_artist">Alle nummers van artiest tonen</string>
|
<string name="settings.show_all_songs_by_artist">Alle nummers van artiest tonen</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Item toevoegen in artiestweergave om alle nummers van een artiest te bekijken</string>
|
<string name="settings.show_all_songs_by_artist_summary">Item toevoegen in artiestweergave om alle nummers van een artiest te bekijken</string>
|
||||||
<string name="download.menu_show_artist">Artiest tonen</string>
|
<string name="download.menu_show_artist">Artiest tonen</string>
|
||||||
|
|
|
@ -343,7 +343,6 @@ ponieważ api Subsonic nie wspiera nowego sposobu autoryzacji dla użytkowników
|
||||||
<string name="share_default_greeting">Sprawdź muzykę, którą udostępniam na %s</string>
|
<string name="share_default_greeting">Sprawdź muzykę, którą udostępniam na %s</string>
|
||||||
<string name="share_via">Udostępnij utwory za pomocą</string>
|
<string name="share_via">Udostępnij utwory za pomocą</string>
|
||||||
<string name="menu.share">Udostępnianie</string>
|
<string name="menu.share">Udostępnianie</string>
|
||||||
<string name="select_album_all_songs">Wszystkie utwory %s</string>
|
|
||||||
<string name="settings.show_all_songs_by_artist">Wyświetlaj wszystkie utwory artysty</string>
|
<string name="settings.show_all_songs_by_artist">Wyświetlaj wszystkie utwory artysty</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Dodaje nową pozycję w widoku artysty z wszystkimi jego utworami</string>
|
<string name="settings.show_all_songs_by_artist_summary">Dodaje nową pozycję w widoku artysty z wszystkimi jego utworami</string>
|
||||||
<string name="download.menu_show_artist">Wyświetlaj artystę</string>
|
<string name="download.menu_show_artist">Wyświetlaj artystę</string>
|
||||||
|
|
|
@ -364,7 +364,6 @@
|
||||||
<string name="share_default_greeting">Confira esta música que compartilhei do %s</string>
|
<string name="share_default_greeting">Confira esta música que compartilhei do %s</string>
|
||||||
<string name="share_via">Compartilhar músicas via</string>
|
<string name="share_via">Compartilhar músicas via</string>
|
||||||
<string name="menu.share">Compartilhar</string>
|
<string name="menu.share">Compartilhar</string>
|
||||||
<string name="select_album_all_songs">Todas as Músicas de %s</string>
|
|
||||||
<string name="settings.show_all_songs_by_artist">Mostrar Todas as Músicas por Artista</string>
|
<string name="settings.show_all_songs_by_artist">Mostrar Todas as Músicas por Artista</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Adicionar nova entrada em artista para acessar todas as músicas do artista</string>
|
<string name="settings.show_all_songs_by_artist_summary">Adicionar nova entrada em artista para acessar todas as músicas do artista</string>
|
||||||
<string name="download.menu_show_artist">Mostrar Artista</string>
|
<string name="download.menu_show_artist">Mostrar Artista</string>
|
||||||
|
|
|
@ -343,7 +343,6 @@
|
||||||
<string name="share_default_greeting">Confira esta música que compartilhei do %s</string>
|
<string name="share_default_greeting">Confira esta música que compartilhei do %s</string>
|
||||||
<string name="share_via">Compartilhar músicas via</string>
|
<string name="share_via">Compartilhar músicas via</string>
|
||||||
<string name="menu.share">Compartilhar</string>
|
<string name="menu.share">Compartilhar</string>
|
||||||
<string name="select_album_all_songs">Todas as Músicas de %s</string>
|
|
||||||
<string name="settings.show_all_songs_by_artist">Todas as Músicas do Artista</string>
|
<string name="settings.show_all_songs_by_artist">Todas as Músicas do Artista</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Adicionar nova entrada em artista para ver todas as músicas do artista</string>
|
<string name="settings.show_all_songs_by_artist_summary">Adicionar nova entrada em artista para ver todas as músicas do artista</string>
|
||||||
<string name="download.menu_show_artist">Mostrar Artista</string>
|
<string name="download.menu_show_artist">Mostrar Artista</string>
|
||||||
|
|
|
@ -362,7 +362,6 @@
|
||||||
<string name="share_default_greeting">Проверьте эту музыку, с которой я поделился %s</string>
|
<string name="share_default_greeting">Проверьте эту музыку, с которой я поделился %s</string>
|
||||||
<string name="share_via">Поделиться треками через</string>
|
<string name="share_via">Поделиться треками через</string>
|
||||||
<string name="menu.share">Поделиться</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">Показать все треки исполнителя</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Добавить новую запись в представлении исполнителя, чтобы получить доступ ко всем песням для исполнителя</string>
|
<string name="settings.show_all_songs_by_artist_summary">Добавить новую запись в представлении исполнителя, чтобы получить доступ ко всем песням для исполнителя</string>
|
||||||
<string name="download.menu_show_artist">Показать исполнителей</string>
|
<string name="download.menu_show_artist">Показать исполнителей</string>
|
||||||
|
|
|
@ -360,7 +360,6 @@
|
||||||
<string name="share_default_greeting">看看我从 %s 分享的这首音乐</string>
|
<string name="share_default_greeting">看看我从 %s 分享的这首音乐</string>
|
||||||
<string name="share_via">分享歌曲通过</string>
|
<string name="share_via">分享歌曲通过</string>
|
||||||
<string name="menu.share">分享</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">按艺术家显示所有歌曲</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">在艺术家视图中添加新条目以访问艺术家的所有歌曲</string>
|
<string name="settings.show_all_songs_by_artist_summary">在艺术家视图中添加新条目以访问艺术家的所有歌曲</string>
|
||||||
<string name="download.menu_show_artist">显示艺术家</string>
|
<string name="download.menu_show_artist">显示艺术家</string>
|
||||||
|
|
|
@ -379,7 +379,6 @@
|
||||||
<string name="share_default_greeting">Check out this music I shared from %s</string>
|
<string name="share_default_greeting">Check out this music I shared from %s</string>
|
||||||
<string name="share_via">Share songs via</string>
|
<string name="share_via">Share songs via</string>
|
||||||
<string name="menu.share">Share</string>
|
<string name="menu.share">Share</string>
|
||||||
<string name="select_album_all_songs">All Songs by %s</string>
|
|
||||||
<string name="settings.show_all_songs_by_artist">Show All Songs By Artist</string>
|
<string name="settings.show_all_songs_by_artist">Show All Songs By Artist</string>
|
||||||
<string name="settings.show_all_songs_by_artist_summary">Add new entry in artist view to access all songs for an artist</string>
|
<string name="settings.show_all_songs_by_artist_summary">Add new entry in artist view to access all songs for an artist</string>
|
||||||
<string name="download.menu_show_artist">Show Artist</string>
|
<string name="download.menu_show_artist">Show Artist</string>
|
||||||
|
|
Loading…
Reference in New Issue