Fixed tag editing for Android 11+ devices
This commit is contained in:
parent
4eb2f68da5
commit
8e64f117f9
8 changed files with 323 additions and 181 deletions
|
@ -21,28 +21,36 @@ import android.graphics.Bitmap
|
|||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.animation.OvershootInterpolator
|
||||
import android.widget.ImageView
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.IntentSenderRequest
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.R.drawable
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.activities.saf.SAFGuideActivity
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.model.ArtworkInfo
|
||||
import code.name.monkey.retromusic.model.LoadingInfo
|
||||
import code.name.monkey.retromusic.model.AudioTagInfo
|
||||
import code.name.monkey.retromusic.repository.Repository
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.SAFUtil
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jaudiotagger.audio.AudioFile
|
||||
import org.jaudiotagger.audio.AudioFileIO
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
|
@ -66,9 +74,12 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
|||
private var savedArtworkInfo: ArtworkInfo? = null
|
||||
private var _binding: VB? = null
|
||||
protected val binding: VB get() = _binding!!
|
||||
private var cacheFiles = listOf<File>()
|
||||
|
||||
abstract val bindingInflater: (LayoutInflater) -> VB
|
||||
|
||||
private lateinit var launcher: ActivityResultLauncher<IntentSenderRequest>
|
||||
|
||||
protected abstract fun loadImageFromFile(selectedFile: Uri?)
|
||||
|
||||
protected val show: AlertDialog
|
||||
|
@ -195,7 +206,6 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
_binding = bindingInflater.invoke(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setStatusbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
|
||||
saveFab = findViewById(R.id.saveTags)
|
||||
|
@ -207,6 +217,11 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
|||
finish()
|
||||
}
|
||||
setUpViews()
|
||||
launcher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
writeToFiles(getSongUris(), cacheFiles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpViews() {
|
||||
|
@ -265,6 +280,8 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
|||
|
||||
protected abstract fun getSongPaths(): List<String>
|
||||
|
||||
protected abstract fun getSongUris(): List<Uri>
|
||||
|
||||
protected fun searchWebFor(vararg keys: String) {
|
||||
val stringBuilder = StringBuilder()
|
||||
for (key in keys) {
|
||||
|
@ -336,23 +353,53 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
|||
|
||||
hideFab()
|
||||
println(fieldKeyValueMap)
|
||||
WriteTagsAsyncTask(this).execute(
|
||||
LoadingInfo(
|
||||
GlobalScope.launch {
|
||||
if (VersionUtils.hasR()) {
|
||||
cacheFiles = TagWriter.writeTagsToFilesR(
|
||||
this@AbsTagEditorActivity, AudioTagInfo(
|
||||
songPaths,
|
||||
fieldKeyValueMap,
|
||||
artworkInfo
|
||||
)
|
||||
)
|
||||
val pendingIntent = MediaStore.createWriteRequest(contentResolver, getSongUris())
|
||||
|
||||
launcher.launch(IntentSenderRequest.Builder(pendingIntent).build())
|
||||
} else {
|
||||
TagWriter.writeTagsToFiles(
|
||||
this@AbsTagEditorActivity, AudioTagInfo(
|
||||
songPaths,
|
||||
fieldKeyValueMap,
|
||||
artworkInfo
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeTags(paths: List<String>?) {
|
||||
WriteTagsAsyncTask(this).execute(
|
||||
LoadingInfo(
|
||||
GlobalScope.launch {
|
||||
if (VersionUtils.hasR()) {
|
||||
cacheFiles = TagWriter.writeTagsToFilesR(
|
||||
this@AbsTagEditorActivity, AudioTagInfo(
|
||||
paths,
|
||||
savedTags,
|
||||
savedArtworkInfo
|
||||
)
|
||||
)
|
||||
val pendingIntent = MediaStore.createWriteRequest(contentResolver, getSongUris())
|
||||
|
||||
launcher.launch(IntentSenderRequest.Builder(pendingIntent).build())
|
||||
} else {
|
||||
TagWriter.writeTagsToFiles(
|
||||
this@AbsTagEditorActivity, AudioTagInfo(
|
||||
paths,
|
||||
savedTags,
|
||||
savedArtworkInfo
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -391,9 +438,30 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun writeToFiles(songUris: List<Uri>, cacheFiles: List<File>) {
|
||||
if (cacheFiles.size == songUris.size) {
|
||||
for (i in cacheFiles.indices) {
|
||||
contentResolver.openOutputStream(songUris[i])?.use { output ->
|
||||
cacheFiles[i].inputStream().use { input ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
TagWriter.scan(this@AbsTagEditorActivity, getSongPaths())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
// Delete Cache Files
|
||||
cacheFiles.forEach { file ->
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val EXTRA_ID = "extra_id"
|
||||
const val EXTRA_PALETTE = "extra_palette"
|
||||
private val TAG = AbsTagEditorActivity::class.java.simpleName
|
||||
|
|
|
@ -38,6 +38,7 @@ import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
|||
import code.name.monkey.retromusic.model.ArtworkInfo
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.ImageUtil
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil.generatePalette
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil.getColor
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
|
@ -193,6 +194,11 @@ class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBindin
|
|||
.map(Song::data)
|
||||
}
|
||||
|
||||
override fun getSongUris(): List<Uri> = repository.albumById(id).songs.map {
|
||||
MusicUtil.getSongFileUri(it.id)
|
||||
}
|
||||
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import code.name.monkey.retromusic.databinding.ActivitySongTagEditorBinding
|
|||
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||
import code.name.monkey.retromusic.extensions.setTint
|
||||
import code.name.monkey.retromusic.repository.SongRepository
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import org.koin.android.ext.android.inject
|
||||
import java.util.*
|
||||
|
@ -111,6 +112,8 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
|
|||
|
||||
override fun getSongPaths(): List<String> = listOf(songRepository.song(id).data)
|
||||
|
||||
override fun getSongUris(): List<Uri> = listOf(MusicUtil.getSongFileUri(id))
|
||||
|
||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
package code.name.monkey.retromusic.activities.tageditor
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.media.MediaScannerConnection
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener
|
||||
import code.name.monkey.retromusic.model.AudioTagInfo
|
||||
import code.name.monkey.retromusic.util.MusicUtil.createAlbumArtFile
|
||||
import code.name.monkey.retromusic.util.MusicUtil.deleteAlbumArt
|
||||
import code.name.monkey.retromusic.util.MusicUtil.insertAlbumArt
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jaudiotagger.audio.AudioFileIO
|
||||
import org.jaudiotagger.audio.exceptions.CannotReadException
|
||||
import org.jaudiotagger.audio.exceptions.CannotWriteException
|
||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException
|
||||
import org.jaudiotagger.tag.TagException
|
||||
import org.jaudiotagger.tag.images.Artwork
|
||||
import org.jaudiotagger.tag.images.ArtworkFactory
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
class TagWriter {
|
||||
|
||||
companion object {
|
||||
|
||||
suspend fun scan(context: Context, toBeScanned: List<String?>?) {
|
||||
if (toBeScanned == null || toBeScanned.isEmpty()) {
|
||||
Log.i("scan", "scan: Empty")
|
||||
Toast.makeText(context, "Scan file from folder", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
MediaScannerConnection.scanFile(
|
||||
context,
|
||||
toBeScanned.toTypedArray(),
|
||||
null,
|
||||
withContext(Dispatchers.Main) {
|
||||
if (context is Activity) UpdateToastMediaScannerCompletionListener(
|
||||
context, toBeScanned
|
||||
) else null
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun writeTagsToFiles(context: Context, info: AudioTagInfo) =
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
var artwork: Artwork? = null
|
||||
var albumArtFile: File? = null
|
||||
if (info.artworkInfo?.artwork != null) {
|
||||
try {
|
||||
albumArtFile = createAlbumArtFile(context).canonicalFile
|
||||
info.artworkInfo.artwork.compress(
|
||||
Bitmap.CompressFormat.PNG,
|
||||
0,
|
||||
FileOutputStream(albumArtFile)
|
||||
)
|
||||
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
var wroteArtwork = false
|
||||
var deletedArtwork = false
|
||||
for (filePath in info.filePaths!!) {
|
||||
try {
|
||||
val audioFile = AudioFileIO.read(File(filePath))
|
||||
val tag = audioFile.tagOrCreateAndSetDefault
|
||||
if (info.fieldKeyValueMap != null) {
|
||||
for ((key, value) in info.fieldKeyValueMap) {
|
||||
try {
|
||||
tag.setField(key, value)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info.artworkInfo != null) {
|
||||
if (info.artworkInfo.artwork == null) {
|
||||
tag.deleteArtworkField()
|
||||
deletedArtwork = true
|
||||
} else if (artwork != null) {
|
||||
tag.deleteArtworkField()
|
||||
tag.setField(artwork)
|
||||
wroteArtwork = true
|
||||
}
|
||||
}
|
||||
audioFile.commit()
|
||||
} catch (e: CannotReadException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: CannotWriteException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: TagException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: ReadOnlyFileException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: InvalidAudioFrameException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
if (wroteArtwork) {
|
||||
insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
|
||||
} else if (deletedArtwork) {
|
||||
deleteAlbumArt(context, info.artworkInfo!!.albumId)
|
||||
}
|
||||
scan(context, info.filePaths)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
scan(context, null)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo) =
|
||||
withContext(Dispatchers.IO) {
|
||||
val cacheFiles = mutableListOf<File>()
|
||||
try {
|
||||
var artwork: Artwork? = null
|
||||
var albumArtFile: File? = null
|
||||
if (info.artworkInfo?.artwork != null) {
|
||||
try {
|
||||
albumArtFile = createAlbumArtFile(context).canonicalFile
|
||||
info.artworkInfo.artwork.compress(
|
||||
Bitmap.CompressFormat.PNG,
|
||||
0,
|
||||
FileOutputStream(albumArtFile)
|
||||
)
|
||||
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
var wroteArtwork = false
|
||||
var deletedArtwork = false
|
||||
for (filePath in info.filePaths!!) {
|
||||
try {
|
||||
val originFile = File(filePath)
|
||||
val cacheFile = File(context.cacheDir, originFile.name)
|
||||
cacheFiles.add(cacheFile)
|
||||
originFile.inputStream().use { input ->
|
||||
cacheFile.outputStream().use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
val audioFile = AudioFileIO.read(cacheFile)
|
||||
val tag = audioFile.tagOrCreateAndSetDefault
|
||||
if (info.fieldKeyValueMap != null) {
|
||||
for ((key, value) in info.fieldKeyValueMap) {
|
||||
try {
|
||||
tag.setField(key, value)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (info.artworkInfo != null) {
|
||||
if (info.artworkInfo.artwork == null) {
|
||||
tag.deleteArtworkField()
|
||||
deletedArtwork = true
|
||||
} else if (artwork != null) {
|
||||
tag.deleteArtworkField()
|
||||
tag.setField(artwork)
|
||||
wroteArtwork = true
|
||||
}
|
||||
}
|
||||
audioFile.commit()
|
||||
} catch (e: CannotReadException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: CannotWriteException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: TagException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: ReadOnlyFileException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: InvalidAudioFrameException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
if (wroteArtwork) {
|
||||
insertAlbumArt(context, info.artworkInfo!!.albumId, albumArtFile!!.path)
|
||||
} else if (deletedArtwork) {
|
||||
deleteAlbumArt(context, info.artworkInfo!!.albumId)
|
||||
}
|
||||
cacheFiles
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
listOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.tageditor;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.jaudiotagger.audio.AudioFile;
|
||||
import org.jaudiotagger.audio.AudioFileIO;
|
||||
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||
import org.jaudiotagger.audio.exceptions.CannotWriteException;
|
||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||
import org.jaudiotagger.tag.FieldKey;
|
||||
import org.jaudiotagger.tag.Tag;
|
||||
import org.jaudiotagger.tag.TagException;
|
||||
import org.jaudiotagger.tag.images.Artwork;
|
||||
import org.jaudiotagger.tag.images.ArtworkFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.misc.DialogAsyncTask;
|
||||
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener;
|
||||
import code.name.monkey.retromusic.model.LoadingInfo;
|
||||
import code.name.monkey.retromusic.util.MusicUtil;
|
||||
|
||||
public class WriteTagsAsyncTask extends DialogAsyncTask<LoadingInfo, Integer, List<String>> {
|
||||
|
||||
public WriteTagsAsyncTask(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> doInBackground(LoadingInfo... params) {
|
||||
try {
|
||||
LoadingInfo info = params[0];
|
||||
|
||||
Artwork artwork = null;
|
||||
File albumArtFile = null;
|
||||
if (info.getArtworkInfo() != null && info.getArtworkInfo().getArtwork() != null) {
|
||||
try {
|
||||
albumArtFile = MusicUtil.INSTANCE.createAlbumArtFile().getCanonicalFile();
|
||||
info.getArtworkInfo().getArtwork().compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile));
|
||||
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
boolean wroteArtwork = false;
|
||||
boolean deletedArtwork = false;
|
||||
for (String filePath : info.getFilePaths()) {
|
||||
publishProgress(++counter, info.getFilePaths().size());
|
||||
try {
|
||||
AudioFile audioFile = AudioFileIO.read(new File(filePath));
|
||||
Tag tag = audioFile.getTagOrCreateAndSetDefault();
|
||||
|
||||
if (info.getFieldKeyValueMap() != null) {
|
||||
for (Map.Entry<FieldKey, String> entry : info.getFieldKeyValueMap().entrySet()) {
|
||||
try {
|
||||
tag.setField(entry.getKey(), entry.getValue());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info.getArtworkInfo() != null) {
|
||||
if (info.getArtworkInfo().getArtwork() == null) {
|
||||
tag.deleteArtworkField();
|
||||
deletedArtwork = true;
|
||||
} else if (artwork != null) {
|
||||
tag.deleteArtworkField();
|
||||
tag.setField(artwork);
|
||||
wroteArtwork = true;
|
||||
}
|
||||
}
|
||||
|
||||
audioFile.commit();
|
||||
} catch (@NonNull CannotReadException | IOException | CannotWriteException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Context context = getContext();
|
||||
if (context != null) {
|
||||
if (wroteArtwork) {
|
||||
MusicUtil.INSTANCE.
|
||||
insertAlbumArt(context, info.getArtworkInfo().getAlbumId(), albumArtFile.getPath());
|
||||
} else if (deletedArtwork) {
|
||||
MusicUtil.INSTANCE.deleteAlbumArt(context, info.getArtworkInfo().getAlbumId());
|
||||
}
|
||||
}
|
||||
|
||||
return info.getFilePaths();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<String> toBeScanned) {
|
||||
super.onPostExecute(toBeScanned);
|
||||
scan(toBeScanned);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled(List<String> toBeScanned) {
|
||||
super.onCancelled(toBeScanned);
|
||||
scan(toBeScanned);
|
||||
}
|
||||
|
||||
private void scan(List<String> toBeScanned) {
|
||||
Context context = getContext();
|
||||
if (toBeScanned == null || toBeScanned.isEmpty()) {
|
||||
Log.i("scan", "scan: Empty");
|
||||
Toast.makeText(context, "Scan file from folder", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
MediaScannerConnection.scanFile(context, toBeScanned.toArray(new String[0]), null, context instanceof Activity ? new UpdateToastMediaScannerCompletionListener((Activity) context, toBeScanned) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog createDialog(@NonNull Context context) {
|
||||
return new MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.saving_changes)
|
||||
.setCancelable(false)
|
||||
.setView(R.layout.loading)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(@NonNull Dialog dialog, Integer... values) {
|
||||
super.onProgressUpdate(dialog, values);
|
||||
// ((MaterialDialog) dialog).setMaxProgress(values[1]);
|
||||
// ((MaterialDialog) dialog).setProgress(values[0]);
|
||||
}
|
||||
}
|
|
@ -14,19 +14,22 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.fragments.other
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.view.*
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.transition.Fade
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.activities.tageditor.WriteTagsAsyncTask
|
||||
import code.name.monkey.retromusic.activities.tageditor.TagWriter
|
||||
import code.name.monkey.retromusic.databinding.FragmentLyricsBinding
|
||||
import code.name.monkey.retromusic.databinding.FragmentNormalLyricsBinding
|
||||
import code.name.monkey.retromusic.databinding.FragmentSyncedLyricsBinding
|
||||
|
@ -37,7 +40,7 @@ import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
|
|||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.lyrics.LrcView
|
||||
import code.name.monkey.retromusic.model.LoadingInfo
|
||||
import code.name.monkey.retromusic.model.AudioTagInfo
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.LyricUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
|
@ -107,6 +110,9 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
|
|||
setupViews()
|
||||
setupToolbar()
|
||||
updateTitleSong()
|
||||
if (VersionUtils.hasR()) {
|
||||
binding.editButton.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupViews() {
|
||||
|
@ -187,6 +193,7 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
|
|||
}
|
||||
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun editNormalLyrics() {
|
||||
var content = ""
|
||||
val file = File(MusicPlayerRemote.currentSong.data)
|
||||
|
@ -205,12 +212,14 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
|
|||
) { _, input ->
|
||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||
fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
|
||||
WriteTagsAsyncTask(requireActivity()).execute(
|
||||
LoadingInfo(
|
||||
GlobalScope.launch {
|
||||
TagWriter.writeTagsToFiles(
|
||||
requireContext(), AudioTagInfo(
|
||||
listOf(song.data), fieldKeyValueMap, null
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
positiveButton(res = R.string.save) {
|
||||
(lyricsSectionsAdapter.fragments[1].first as NormalLyrics).loadNormalLyrics()
|
||||
}
|
||||
|
@ -219,6 +228,7 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
|
|||
}
|
||||
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun editSyncedLyrics() {
|
||||
var lrcFile: File? = null
|
||||
if (LyricUtil.isLrcOriginalFileExist(song.data)) {
|
||||
|
|
|
@ -2,7 +2,7 @@ package code.name.monkey.retromusic.model
|
|||
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
|
||||
class LoadingInfo(
|
||||
class AudioTagInfo(
|
||||
val filePaths: List<String>?,
|
||||
val fieldKeyValueMap: Map<FieldKey, String>?,
|
||||
val artworkInfo: ArtworkInfo?
|
|
@ -17,6 +17,7 @@ import android.widget.Toast
|
|||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||
import code.name.monkey.retromusic.db.SongEntity
|
||||
|
@ -75,15 +76,18 @@ object MusicUtil : KoinComponent {
|
|||
return if (string2.isNullOrEmpty()) if (string1.isNullOrEmpty()) "" else string1 else "$string1 • $string2"
|
||||
}
|
||||
|
||||
fun createAlbumArtFile(): File {
|
||||
fun createAlbumArtFile(context: Context): File {
|
||||
return File(
|
||||
createAlbumArtDir(),
|
||||
createAlbumArtDir(context),
|
||||
System.currentTimeMillis().toString()
|
||||
)
|
||||
}
|
||||
|
||||
private fun createAlbumArtDir(): File {
|
||||
val albumArtDir = File(Environment.getExternalStorageDirectory(), "/albumthumbs/")
|
||||
private fun createAlbumArtDir(context: Context): File {
|
||||
val albumArtDir = File(
|
||||
if (VersionUtils.hasR()) context.cacheDir else Environment.getExternalStorageDirectory(),
|
||||
"/albumthumbs/"
|
||||
)
|
||||
if (!albumArtDir.exists()) {
|
||||
albumArtDir.mkdirs()
|
||||
try {
|
||||
|
@ -520,7 +524,7 @@ object MusicUtil : KoinComponent {
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
fun deleteTracksQ(activity: Activity, songs: List<Song>) {
|
||||
val pendingIntent = MediaStore.createDeleteRequest(activity.contentResolver, songs.map {
|
||||
getSongFileUri(it.id)
|
||||
|
|
Loading…
Reference in a new issue