Fixed Playlist save
This commit is contained in:
parent
9a32aa2805
commit
e9b7b5a203
5 changed files with 129 additions and 33 deletions
|
@ -21,13 +21,16 @@ import android.widget.Toast
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||||
import code.name.monkey.retromusic.App
|
import code.name.monkey.retromusic.App
|
||||||
import code.name.monkey.retromusic.EXTRA_PLAYLIST
|
import code.name.monkey.retromusic.EXTRA_PLAYLIST
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
||||||
import code.name.monkey.retromusic.extensions.colorButtons
|
import code.name.monkey.retromusic.extensions.colorButtons
|
||||||
|
import code.name.monkey.retromusic.extensions.createNewFile
|
||||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||||
import code.name.monkey.retromusic.extensions.materialDialog
|
import code.name.monkey.retromusic.extensions.materialDialog
|
||||||
|
import code.name.monkey.retromusic.helper.M3UWriter
|
||||||
import code.name.monkey.retromusic.util.PlaylistsUtil
|
import code.name.monkey.retromusic.util.PlaylistsUtil
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -46,22 +49,59 @@ class SavePlaylistDialog : DialogFragment() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
val playlistWithSongs = extraNotNull<PlaylistWithSongs>(EXTRA_PLAYLIST).value
|
||||||
val playlistWithSongs = extraNotNull<PlaylistWithSongs>(EXTRA_PLAYLIST).value
|
|
||||||
val file = PlaylistsUtil.savePlaylistWithSongs(playlistWithSongs)
|
if (VersionUtils.hasR()) {
|
||||||
MediaScannerConnection.scanFile(
|
createNewFile(
|
||||||
requireActivity(),
|
"audio/mpegurl",
|
||||||
arrayOf<String>(file.path),
|
playlistWithSongs.playlistEntity.playlistName
|
||||||
null
|
) { outputStream, data ->
|
||||||
) { _, _ ->
|
try {
|
||||||
|
if (outputStream != null) {
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
M3UWriter.writeIO(
|
||||||
|
outputStream,
|
||||||
|
playlistWithSongs
|
||||||
|
)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(
|
||||||
|
requireContext(),
|
||||||
|
String.format(
|
||||||
|
requireContext().getString(R.string.saved_playlist_to),
|
||||||
|
data?.lastPathSegment
|
||||||
|
),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
"Something went wrong : " + e.message,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
} else {
|
||||||
Toast.makeText(
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
requireContext(),
|
val file = PlaylistsUtil.savePlaylistWithSongs(playlistWithSongs)
|
||||||
String.format(App.getContext().getString(R.string.saved_playlist_to), file),
|
MediaScannerConnection.scanFile(
|
||||||
Toast.LENGTH_LONG
|
requireActivity(),
|
||||||
).show()
|
arrayOf<String>(file.path),
|
||||||
dismiss()
|
null
|
||||||
|
) { _, _ ->
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(
|
||||||
|
requireContext(),
|
||||||
|
String.format(App.getContext().getString(R.string.saved_playlist_to), file),
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package code.name.monkey.retromusic.extensions
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Environment
|
||||||
|
import android.provider.DocumentsContract
|
||||||
|
import androidx.activity.result.ActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import java.io.File
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
fun Fragment.createNewFile(
|
||||||
|
mimeType: String,
|
||||||
|
fileName: String,
|
||||||
|
write: (outputStream: OutputStream?, data: Uri?) -> Unit
|
||||||
|
) {
|
||||||
|
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
intent.type = mimeType
|
||||||
|
intent.putExtra(Intent.EXTRA_TITLE, fileName)
|
||||||
|
val startForResult =
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
startForResult.launch(intent)
|
||||||
|
}
|
|
@ -18,10 +18,7 @@ import code.name.monkey.retromusic.db.PlaylistWithSongs
|
||||||
import code.name.monkey.retromusic.db.toSongs
|
import code.name.monkey.retromusic.db.toSongs
|
||||||
import code.name.monkey.retromusic.model.Playlist
|
import code.name.monkey.retromusic.model.Playlist
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import java.io.BufferedWriter
|
import java.io.*
|
||||||
import java.io.File
|
|
||||||
import java.io.FileWriter
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
object M3UWriter : M3UConstants {
|
object M3UWriter : M3UConstants {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -69,4 +66,23 @@ object M3UWriter : M3UConstants {
|
||||||
}
|
}
|
||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun writeIO(outputStream: OutputStream, playlistWithSongs: PlaylistWithSongs) {
|
||||||
|
val songs: List<Song> = playlistWithSongs.songs.sortedBy {
|
||||||
|
it.songPrimaryKey
|
||||||
|
}.toSongs()
|
||||||
|
if (songs.isNotEmpty()) {
|
||||||
|
val bufferedWriter = outputStream.bufferedWriter()
|
||||||
|
bufferedWriter.write(M3UConstants.HEADER)
|
||||||
|
songs.forEach {
|
||||||
|
bufferedWriter.newLine()
|
||||||
|
bufferedWriter.write(M3UConstants.ENTRY + it.duration + M3UConstants.DURATION_SEPARATOR + it.artistName + " - " + it.title)
|
||||||
|
bufferedWriter.newLine()
|
||||||
|
bufferedWriter.write(it.data)
|
||||||
|
}
|
||||||
|
bufferedWriter.close()
|
||||||
|
}
|
||||||
|
outputStream.flush()
|
||||||
|
outputStream.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ import java.util.List;
|
||||||
|
|
||||||
import code.name.monkey.retromusic.R;
|
import code.name.monkey.retromusic.R;
|
||||||
import code.name.monkey.retromusic.db.PlaylistWithSongs;
|
import code.name.monkey.retromusic.db.PlaylistWithSongs;
|
||||||
|
import code.name.monkey.retromusic.helper.M3UConstants;
|
||||||
import code.name.monkey.retromusic.helper.M3UWriter;
|
import code.name.monkey.retromusic.helper.M3UWriter;
|
||||||
import code.name.monkey.retromusic.model.Playlist;
|
import code.name.monkey.retromusic.model.Playlist;
|
||||||
import code.name.monkey.retromusic.model.PlaylistSong;
|
import code.name.monkey.retromusic.model.PlaylistSong;
|
||||||
|
@ -319,9 +320,9 @@ public class PlaylistsUtil {
|
||||||
private static boolean doesPlaylistExist(
|
private static boolean doesPlaylistExist(
|
||||||
@NonNull Context context, @NonNull final String selection, @NonNull final String[] values) {
|
@NonNull Context context, @NonNull final String selection, @NonNull final String[] values) {
|
||||||
Cursor cursor =
|
Cursor cursor =
|
||||||
context
|
context
|
||||||
.getContentResolver()
|
.getContentResolver()
|
||||||
.query(EXTERNAL_CONTENT_URI, new String[] {}, selection, values, null);
|
.query(EXTERNAL_CONTENT_URI, new String[]{}, selection, values, null);
|
||||||
|
|
||||||
boolean exists = false;
|
boolean exists = false;
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
|
|
|
@ -8,18 +8,11 @@ import android.os.Build
|
||||||
|
|
||||||
object VersionUtils {
|
object VersionUtils {
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if device is running API >= 21
|
|
||||||
*/
|
|
||||||
fun hasLollipop(): Boolean {
|
|
||||||
return Build.VERSION.SDK_INT >= 21
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if device is running API >= 23
|
* @return true if device is running API >= 23
|
||||||
*/
|
*/
|
||||||
fun hasMarshmallow(): Boolean {
|
fun hasMarshmallow(): Boolean {
|
||||||
return Build.VERSION.SDK_INT >= 23
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,7 +23,7 @@ object VersionUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if device is running API >= 24
|
* @return true if device is running API >= 25
|
||||||
*/
|
*/
|
||||||
fun hasNougatMR(): Boolean {
|
fun hasNougatMR(): Boolean {
|
||||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1
|
||||||
|
@ -44,20 +37,31 @@ object VersionUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if device is running API >= 27
|
* @return true if device is running API >= 28
|
||||||
*/
|
*/
|
||||||
fun hasP(): Boolean {
|
fun hasP(): Boolean {
|
||||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if device is running API >= 28
|
* @return true if device is running API >= 29
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun hasQ(): Boolean {
|
fun hasQ(): Boolean {
|
||||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if device is running API >= 30
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun hasR(): Boolean {
|
||||||
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if device is running API >= 31
|
||||||
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun hasS(): Boolean {
|
fun hasS(): Boolean {
|
||||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||||
|
|
Loading…
Reference in a new issue