[Lyrics] Added lyrics editing on A11+ devices

This commit is contained in:
Prathamesh More 2022-01-18 20:48:18 +05:30
parent 39a2c45081
commit af4347c4ff
6 changed files with 147 additions and 14 deletions

View file

@ -21,9 +21,9 @@ fun Fragment.createNewFile(
registerForActivityResult(ActivityResultContracts.StartActivityForResult())
{ result: ActivityResult ->
if (result.resultCode == Activity.RESULT_OK) {
val outputStream: OutputStream? =
context?.contentResolver?.openOutputStream(result.data?.data!!)
write(outputStream, result.data?.data)
context?.contentResolver?.openOutputStream(result.data?.data!!)?.use { os->
write(os, result.data?.data)
}
}
}

View file

@ -0,0 +1,6 @@
package code.name.monkey.retromusic.extensions
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil
val Song.uri get() = MusicUtil.getSongFileUri(songId = id)

View file

@ -15,9 +15,16 @@
package code.name.monkey.retromusic.fragments.other
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.text.InputType
import android.view.*
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
@ -36,14 +43,14 @@ import code.name.monkey.retromusic.databinding.FragmentSyncedLyricsBinding
import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.extensions.textColorSecondary
import code.name.monkey.retromusic.extensions.uri
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.AudioTagInfo
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.LyricUtil
import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.*
import com.afollestad.materialdialogs.LayoutMode
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
@ -55,6 +62,7 @@ import kotlinx.coroutines.*
import org.jaudiotagger.audio.AudioFileIO
import org.jaudiotagger.tag.FieldKey
import java.io.File
import java.io.FileOutputStream
import java.util.*
class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
@ -95,6 +103,43 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
return transform
}
private lateinit var normalLyricsLauncher: ActivityResultLauncher<IntentSenderRequest>
private lateinit var newSyncedLyricsLauncher: ActivityResultLauncher<Intent>
private lateinit var editSyncedLyricsLauncher: ActivityResultLauncher<IntentSenderRequest>
private lateinit var cacheFile: File
private var syncedLyrics: String = ""
private lateinit var syncedFileUri: Uri
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Normal lyrics launcher
normalLyricsLauncher =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
FileUtils.copyFileToUri(requireContext(), cacheFile, song.uri)
}
}
newSyncedLyricsLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
context?.contentResolver?.openOutputStream(result.data?.data!!)?.use {
it.write(syncedLyrics.toByteArray())
}
}
}
editSyncedLyricsLauncher =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
requireContext().contentResolver.openOutputStream(syncedFileUri)?.use { os ->
(os as FileOutputStream).channel.truncate(0)
os.write(syncedLyrics.toByteArray())
os.flush()
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
@ -111,9 +156,6 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
setupViews()
setupToolbar()
updateTitleSong()
if (VersionUtils.hasR()) {
binding.editButton.isVisible = false
}
}
private fun setupViews() {
@ -213,7 +255,24 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
) { _, input ->
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
syncedLyrics = input.toString()
GlobalScope.launch {
if (VersionUtils.hasR()) {
cacheFile = TagWriter.writeTagsToFilesR(
requireContext(), AudioTagInfo(
listOf(song.data), fieldKeyValueMap, null
)
)[0]
val pendingIntent =
MediaStore.createWriteRequest(
requireContext().contentResolver,
listOf(song.uri)
)
normalLyricsLauncher.launch(
IntentSenderRequest.Builder(pendingIntent).build()
)
} else {
TagWriter.writeTagsToFiles(
requireContext(), AudioTagInfo(
listOf(song.data), fieldKeyValueMap, null
@ -221,6 +280,7 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
)
}
}
}
positiveButton(res = R.string.save) {
(lyricsSectionsAdapter.fragments[1].first as NormalLyrics).loadNormalLyrics()
}
@ -240,8 +300,34 @@ class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
prefill = content,
inputType = InputType.TYPE_TEXT_FLAG_MULTI_LINE or InputType.TYPE_CLASS_TEXT
) { _, input ->
if (VersionUtils.hasR()) {
syncedLyrics = input.toString()
val lrcFile = LyricUtil.getSyncedLyricsFile(song)
if (lrcFile?.exists() == true) {
syncedFileUri =
UriUtil.getUriFromPath(requireContext(), lrcFile.absolutePath)
val pendingIntent =
MediaStore.createWriteRequest(
requireContext().contentResolver,
listOf(syncedFileUri)
)
editSyncedLyricsLauncher.launch(
IntentSenderRequest.Builder(pendingIntent).build()
)
} else {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
intent.putExtra(
Intent.EXTRA_TITLE,
LyricUtil.getLrcOriginalPath(File(song.data).name)
)
newSyncedLyricsLauncher.launch(intent)
}
} else {
LyricUtil.writeLrc(song, input.toString())
}
}
positiveButton(res = R.string.save) {
(lyricsSectionsAdapter.fragments[0].first as SyncedLyrics).loadLRCLyrics()
}

View file

@ -0,0 +1,16 @@
package code.name.monkey.retromusic.util
import android.content.Context
import android.net.Uri
import java.io.File
object FileUtils {
fun copyFileToUri(context: Context, fromFile: File, toUri: Uri) {
context.contentResolver.openOutputStream(toUri)
?.use { output ->
fromFile.inputStream().use { input ->
input.copyTo(output)
}
}
}
}

View file

@ -118,7 +118,7 @@ object LyricUtil {
return "$lrcRootPath$title - $artist.lrc"
}
private fun getLrcOriginalPath(filePath: String): String {
fun getLrcOriginalPath(filePath: String): String {
return filePath.replace(filePath.substring(filePath.lastIndexOf(".") + 1), "lrc")
}

View file

@ -0,0 +1,25 @@
package code.name.monkey.retromusic.util
import android.content.ContentUris
import android.content.Context
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import androidx.annotation.RequiresApi
object UriUtil {
@RequiresApi(Build.VERSION_CODES.Q)
fun getUriFromPath(context: Context, path: String): Uri {
val uri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
val proj = arrayOf(MediaStore.Files.FileColumns._ID)
context.contentResolver.query(
uri, proj, MediaStore.Files.FileColumns.DATA + "=?", arrayOf(path), null
)?.use { cursor ->
if (cursor.count != 0) {
cursor.moveToFirst()
return ContentUris.withAppendedId(uri, cursor.getLong(0))
}
}
return Uri.EMPTY
}
}