diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f9cf67e..b282a17 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -34,8 +34,11 @@
android:windowSoftInputMode="adjustResize">
+
+
+
@@ -65,6 +68,36 @@
android:scheme="https"
tools:ignore="IntentFilterUniqueDataAttributes" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/app/suhasdissa/vibeyou/MainActivity.kt b/app/src/main/java/app/suhasdissa/vibeyou/MainActivity.kt
index a6f86d3..1ba8da7 100644
--- a/app/src/main/java/app/suhasdissa/vibeyou/MainActivity.kt
+++ b/app/src/main/java/app/suhasdissa/vibeyou/MainActivity.kt
@@ -104,8 +104,11 @@ class MainActivity : ComponentActivity() {
private fun handleIntent(intent: Intent) {
when (intent.action) {
Intent.ACTION_VIEW -> {
- val uri = intent.data
- uri?.let {
+ val uri = intent.data ?: return
+ // Check if uri points to a device file
+ if (uri.scheme == "file" || uri.scheme == "content") {
+ playerViewModel.tryToPlayUri(uri)
+ } else {
processLink(uri)
}
}
diff --git a/app/src/main/java/app/suhasdissa/vibeyou/backend/repository/LocalMusicRepository.kt b/app/src/main/java/app/suhasdissa/vibeyou/backend/repository/LocalMusicRepository.kt
index 3da4073..83b79f3 100644
--- a/app/src/main/java/app/suhasdissa/vibeyou/backend/repository/LocalMusicRepository.kt
+++ b/app/src/main/java/app/suhasdissa/vibeyou/backend/repository/LocalMusicRepository.kt
@@ -5,6 +5,7 @@ import android.content.ContentResolver
import android.content.ContentUris
import android.net.Uri
import android.os.Build
+import android.os.Environment.getExternalStorageDirectory
import android.provider.MediaStore
import android.text.format.DateUtils
import androidx.core.net.toUri
@@ -16,6 +17,7 @@ import app.suhasdissa.vibeyou.backend.database.entities.SearchQuery
import app.suhasdissa.vibeyou.utils.Pref
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
+import java.io.File
class LocalMusicRepository(
private val contentResolver: ContentResolver,
@@ -26,6 +28,7 @@ class LocalMusicRepository(
private var artistCache = listOf()
suspend fun getAllSongs(): List = withContext(Dispatchers.IO) {
+
if (songsCache.isNotEmpty()) return@withContext songsCache
val songs = mutableListOf()
@@ -74,7 +77,8 @@ class LocalMusicRepository(
MediaStore.Audio.Media.DATE_MODIFIED
)
val dateAddedColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATE_ADDED)
- val trackNumberColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.CD_TRACK_NUMBER)
+ val trackNumberColumn =
+ cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.CD_TRACK_NUMBER)
while (cursor.moveToNext()) {
val id = cursor.getLong(idColumn)
@@ -122,6 +126,136 @@ class LocalMusicRepository(
songs
}
+ suspend fun getSongFromUri(uri: Uri): Song? = withContext(Dispatchers.IO) {
+ val collection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ MediaStore.Audio.Media.getContentUri(
+ MediaStore.VOLUME_EXTERNAL
+ )
+ } else {
+ MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
+ }
+
+ val projection = arrayOf(
+ MediaStore.Audio.Media._ID,
+ MediaStore.Audio.Media.TITLE,
+ MediaStore.Audio.Media.DISPLAY_NAME,
+ MediaStore.Audio.Media.DURATION,
+ MediaStore.Audio.Media.ARTIST,
+ MediaStore.Audio.Media.ALBUM_ID,
+ MediaStore.Audio.Media.ARTIST_ID,
+ MediaStore.Audio.Media.DATE_MODIFIED,
+ MediaStore.Audio.Media.DATE_ADDED,
+ MediaStore.Audio.Media.CD_TRACK_NUMBER
+ )
+
+ val selection = "${MediaStore.Audio.Media.DATA} = ?"
+
+ val path = getPathFromContentUri(uri)
+ val selectionArgs = arrayOf(path)
+
+ val query = contentResolver.query(
+ collection,
+ projection,
+ selection,
+ selectionArgs,
+ null
+ )
+
+ query?.use { cursor ->
+ val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)
+ val titleColumn =
+ cursor.getColumnIndex(MediaStore.Audio.Media.TITLE)
+ val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME)
+ val durationColumn =
+ cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)
+ val artistColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)
+ val albumColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID)
+ val artistIdColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID)
+ val creationDateColumn = cursor.getColumnIndexOrThrow(
+ MediaStore.Audio.Media.DATE_MODIFIED
+ )
+ val dateAddedColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATE_ADDED)
+ val trackNumberColumn =
+ cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.CD_TRACK_NUMBER)
+
+ if (cursor.moveToFirst()) {
+ val id = cursor.getLong(idColumn)
+ val name =
+ if (cursor.isNull(titleColumn)) {
+ cursor.getString(nameColumn)
+ } else {
+ cursor.getString(
+ titleColumn
+ )
+ }
+ val albumId = cursor.getLong(albumColumn)
+
+ val contentUri: Uri = ContentUris.withAppendedId(
+ MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ id
+ )
+
+ val duration = cursor.getLong(durationColumn) / 1000
+ if (duration == 0L) return@withContext null
+
+ return@withContext Song(
+ id = contentUri.toString(),
+ title = name,
+ durationText = DateUtils.formatElapsedTime(duration),
+ thumbnailUri = getAlbumArt(albumId),
+ artistsText = cursor.getString(artistColumn),
+ albumId = albumId,
+ artistId = cursor.getLong(artistIdColumn),
+ isLocal = true,
+ creationDate = cursor.getLong(creationDateColumn),
+ dateAdded = cursor.getLong(dateAddedColumn),
+ trackNumber = cursor.getLong(trackNumberColumn)
+ )
+ }
+ }
+ return@withContext null
+ }
+
+ private fun getPathFromContentUri(uri: Uri): String? {
+ var songFile: File? = null
+ if (uri.authority == "com.android.externalstorage.documents") {
+ val path = uri.path?.split(":".toRegex(), 2)?.get(1)
+ if (path != null) {
+ songFile = File(getExternalStorageDirectory(), path)
+ }
+ }
+ if (songFile == null) {
+ val path = getFilePathFromUri(uri)
+ if (path != null)
+ songFile = File(path)
+ }
+ if (songFile == null && uri.path != null) {
+ songFile = File(uri.path!!)
+ }
+ if (songFile != null) {
+ return songFile.absolutePath
+ }
+ return null
+ }
+
+ private fun getFilePathFromUri(uri: Uri): String? {
+ val column = MediaStore.Audio.Media.DATA
+ val projection = arrayOf(column)
+ val query = contentResolver.query(uri, projection, null, null, null)
+
+ query?.use { cursor ->
+ try {
+ if (cursor.moveToFirst()) {
+ val columnIndex = cursor.getColumnIndexOrThrow(column)
+ return cursor.getString(columnIndex)
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ return null
+ }
+
suspend fun getAllAlbums(): List = withContext(Dispatchers.IO) {
if (albumCache.isNotEmpty()) return@withContext albumCache
@@ -279,6 +413,7 @@ class LocalMusicRepository(
searchDao.addSearchQuery(SearchQuery(id = 0, query))
}
+
fun deleteQuery(query: String) = searchDao.deleteQuery(query)
fun getSearchHistory() = searchDao.getSearchHistory()
diff --git a/app/src/main/java/app/suhasdissa/vibeyou/backend/viewmodel/PlayerViewModel.kt b/app/src/main/java/app/suhasdissa/vibeyou/backend/viewmodel/PlayerViewModel.kt
index 6d81b67..7432a12 100644
--- a/app/src/main/java/app/suhasdissa/vibeyou/backend/viewmodel/PlayerViewModel.kt
+++ b/app/src/main/java/app/suhasdissa/vibeyou/backend/viewmodel/PlayerViewModel.kt
@@ -1,5 +1,6 @@
package app.suhasdissa.vibeyou.backend.viewmodel
+import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.util.Log
@@ -17,6 +18,7 @@ import androidx.media3.common.PlaybackParameters
import androidx.media3.session.MediaController
import app.suhasdissa.vibeyou.MellowMusicApplication
import app.suhasdissa.vibeyou.backend.data.Song
+import app.suhasdissa.vibeyou.backend.repository.LocalMusicRepository
import app.suhasdissa.vibeyou.backend.repository.PipedMusicRepository
import app.suhasdissa.vibeyou.backend.repository.SongDatabaseRepository
import app.suhasdissa.vibeyou.utils.addNext
@@ -33,6 +35,7 @@ import kotlinx.coroutines.launch
class PlayerViewModel(
private val songDatabaseRepository: SongDatabaseRepository,
private val musicRepository: PipedMusicRepository,
+ private val localMusicRepository: LocalMusicRepository,
private val controllerFuture: ListenableFuture
) :
ViewModel() {
@@ -148,6 +151,19 @@ class PlayerViewModel(
}
}
+ fun tryToPlayUri(uri: Uri) {
+ viewModelScope.launch {
+ val song: Song? = localMusicRepository.getSongFromUri(uri)
+ song?.let {
+ if (controller == null) {
+ toBePlayed = song.asMediaItem
+ } else {
+ controller?.forcePlay(song.asMediaItem)
+ }
+ }
+ }
+ }
+
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
@@ -155,6 +171,7 @@ class PlayerViewModel(
PlayerViewModel(
application.container.songDatabaseRepository,
application.container.pipedMusicRepository,
+ application.container.localMusicRepository,
application.container.controllerFuture
)
}