Skip to content
Draft

Idfk #29

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions app/src/main/java/com/example/findingwav/Exporter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package com.example.findingwav

import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.app.DownloadManager
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.media3.common.MediaItem
import androidx.media3.common.util.UnstableApi
import java.io.File
import java.io.OutputStream

/** Writes to downloads using mediastore api */
@RequiresApi(Build.VERSION_CODES.Q)
fun savePlaylistToDownloads(context: Context, playlistName: String, content: String) {
try {
val resolver = context.contentResolver
val fileName = "$playlistName.m3u"

// 1. Setup the file details
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
put(MediaStore.MediaColumns.MIME_TYPE, "audio/x-mpegurl")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
}

// 2. Ask MediaStore to create the file entry
// This works even on Android 11+ without special permissions
val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)

if (uri != null) {
// 3. Open the stream and write the data
val outputStream: OutputStream? = resolver.openOutputStream(uri)
outputStream?.use { stream ->
stream.write(content.toByteArray())
}

// 4. Success! Show Toast
Toast.makeText(context, "Saved $fileName to Downloads", Toast.LENGTH_SHORT).show()

// 5. Open Downloads App
openDownloadsFolder(context)
} else {
Toast.makeText(context, "Failed to create file", Toast.LENGTH_SHORT).show()
}

} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(context, "Error: ${e.message}", Toast.LENGTH_LONG).show()
}
}

// Helper to open the folder
fun openDownloadsFolder(context: Context) {
try {
val intent = Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
}
context.startActivity(intent)
} catch (e: Exception) {
// Fallback for some devices that don't support this intent
println("Could not open downloads: ${e.message}")
}
}

fun testM3U() {
var testSong: MainActivity.Audio = MainActivity.Audio(
Uri.parse("Music/Aja - Steely Dan (320).mp3"),
"Music/ Steely Dan - Aja",
"Album",
"Aja",
"Steely Dan",
480
)

var playlist: MutableList<MainActivity.Audio> = mutableListOf<MainActivity.Audio>()
playlist.add(testSong)

//println(toM3U("Main", playlist))
}


/**
* Format is
* #EXTM3U *Initialiser*
* #EXTINF:RUNTIME(seconds),(noSpace)ARTIST_NAME - SONG NAME
* FILEPATH/FILENAME
*
* example:
* #EXTM3U
* #EXTINF:480,Steely Dan - Aja
* Music/Aja - Steely Dan (320).mp3
*
*
* */
@RequiresApi(Build.VERSION_CODES.Q)
@androidx.annotation.OptIn(UnstableApi::class)
fun toM3U(playlistName: String, playlist: MutableList<MediaItem>?, context: Context) {
// grab a playlist
var out: StringBuilder = StringBuilder()

out.append("#EXTM3U\n")
//val path = Environment.getExternalStoragePublicDirectory("Music"
val path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
if (playlist != null) {
for (song in playlist) {
var metaData = song.mediaMetadata
// Using metaData.durationMS here would necessitate deprecated/experimental stuff
// But easier than bringing the music player all the way here
//
out.append("#EXTINF:").append(song.mediaMetadata.durationMs?.div(1000) ?: 1).append(",")
.append(metaData.artist.toString())
.append(" - ")
// Title is the actual name of the song (maybe switch with display title)
.append(metaData.title).append("\n")
// Display title is the file name
out.append(path).append("/").append(song.mediaMetadata.displayTitle).append("\n")
}
}
//
// file.writeText(out.toString())
savePlaylistToDownloads(context, playlistName, out.toString())

Toast.makeText(context, "Playlist saved successfully!", Toast.LENGTH_SHORT).show()
openDownloadsFolder(context)
}

101 changes: 16 additions & 85 deletions app/src/main/java/com/example/findingwav/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,18 @@ import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors
import android.content.ComponentName

// used to open m3u in downloads
import android.app.DownloadManager
import android.media.MediaScannerConnection
import android.app.Activity
import android.content.ContentValues
import java.io.OutputStream

class MainActivity : AppCompatActivity() {
private var player: Player? = null // Use generic Player interface; NOT TO BE CONFUSED WITH PLAYER.KT.
private var controllerFuture: ListenableFuture<MediaController>? = null
private var musicPlayerWrapper: MusicPlayer? = null
private val CUTOFFTIME: Long = 60000

// Define what happens after the user clicks "Allow" or "Deny"
private val requestMusicPermissionLauncher = registerForActivityResult(
Expand Down Expand Up @@ -115,7 +123,13 @@ class MainActivity : AppCompatActivity() {
val controller = controllerFuture?.get()

if (controller != null) {
musicPlayerWrapper = MusicPlayer(controller)
// if music player alr exists, no need to wipe everything
if (musicPlayerWrapper == null) {
musicPlayerWrapper = MusicPlayer(controller)
} else {
musicPlayerWrapper?.player = controller
}


// Check if we have permission AND if we need to load music
val hasPermission = ContextCompat.checkSelfPermission(
Expand Down Expand Up @@ -224,7 +238,7 @@ class MainActivity : AppCompatActivity() {
while (cursor.moveToNext()) {
val isMusic = cursor.getString(music)
// Check that file is music file
if (isMusic.isNotEmpty()) {
if (isMusic.isNotEmpty() && cursor.getLong(durationColumn) > CUTOFFTIME) {
// Assign the values of the files to these
val id = cursor.getLong(idColumn)
val name = cursor.getString(nameColumn)
Expand Down Expand Up @@ -392,87 +406,4 @@ fun TitlePreview() {
}
}

fun testM3U() {
var testSong: MainActivity.Audio = MainActivity.Audio(
Uri.parse("Music/Aja - Steely Dan (320).mp3"),
"Music/ Steely Dan - Aja",
"Album",
"Aja",
"Steely Dan",
480
)

var playlist: MutableList<MainActivity.Audio> = mutableListOf<MainActivity.Audio>()
playlist.add(testSong)

//println(toM3U("Main", playlist))
}


/**To be used to create the .m3u file into files. Maybe works. Needs to change some params*/
// pass in playlistName
// context is applicationContext
fun createFile(playlistName: String, playlist: String, context: Context/*TODO: CHANGE THIS*/)
{
// Request code for creating a PDF document.
//val path = context.getExternalFilesDir(null)
val path = Environment.getExternalStoragePublicDirectory("Music")
File(path, "$playlistName" + ".m3u").delete()
println("Path: " + path)
// TODO: Add name of playlist file
var playlistFile = File(path, "$playlistName" + ".m3u")
// TODO: actually put playlist content, try a forEach or idk

playlistFile.writeText("$playlist")

}


/**
* Format is
* #EXTM3U *Initialiser*
* #EXTINF:RUNTIME(seconds),(noSpace)ARTIST_NAME - SONG NAME
* FILEPATH/FILENAME
*
* example:
* #EXTM3U
* #EXTINF:480,Steely Dan - Aja
* Music/Aja - Steely Dan (320).mp3
*
*
* */
@androidx.annotation.OptIn(UnstableApi::class)
fun toM3U(playlistName: String, playlist: MutableList<MediaItem>?, context: Context) : String {
// grab a playlist
var out: StringBuilder = StringBuilder()

out.append("#EXTM3U\n")
val path = Environment.getExternalStoragePublicDirectory("Music")
if (playlist != null) {
for (song in playlist) {
var metaData = song.mediaMetadata
// Using metaData.durationMS here would necessitate deprecated/experimental stuff
// But easier than bringing the music player all the way here
//
out.append("#EXTINF:").append(song.mediaMetadata.durationMs?.div(1000) ?: 1).append(",")
.append(metaData.artist.toString())
.append(" - ")
// Title is the actual name of the song (maybe switch with display title)
.append(metaData.title).append("\n")
// Display title is the file name
out.append(path).append("/").append(song.mediaMetadata.displayTitle).append("\n")
}
}
// attempt to write locally to downloads?
// val filePath: String = "Playlists/$playlistName"
// val file = File(filePath)
//
// file.writeText(out.toString())
createFile(playlistName, out.toString(), context)


println("Line written successfully")

return out.toString()

}
3 changes: 2 additions & 1 deletion app/src/main/java/com/example/findingwav/Player.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public enum class NextOpts {
* @param player the music player to be used
* @param songs the songs to add to the music player to play
*/
public class MusicPlayer(val player: Player, songs: List<MediaItem>? = null) {
public class MusicPlayer(var player: Player, songs: List<MediaItem>? = null) {

private var exoSongList: MutableList<MediaItem> = mutableListOf()
private var songCount : Int = 0
Expand Down Expand Up @@ -136,6 +136,7 @@ public class MusicPlayer(val player: Player, songs: List<MediaItem>? = null) {
*/
public fun setCurrentPlaylist(name: String) : Boolean {
if (getPlaylist(name) != null) {
// only set if it exists
currentPlaylist = getPlaylist(name)!!
return true
}
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/java/com/example/findingwav/data/datamanager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ public var NECESSARY_PLAYTIME : Double = 0.9;
/**
* Whether adding multiple of the same song is allowed in the playlist
*/
public var REPEAT_SONGS : Boolean = false
public var REPEAT_SONGS : Boolean = false

public var DEBUG : Boolean = true
Loading