Skip to content

Commit

Permalink
add delete local song option
Browse files Browse the repository at this point in the history
  • Loading branch information
IamRezaMousavi committed Jul 4, 2024
1 parent 28ee183 commit 6dba4ae
Show file tree
Hide file tree
Showing 11 changed files with 81 additions and 19 deletions.
20 changes: 16 additions & 4 deletions app/schemas/app.banafsh.android.DatabaseInitializer/1.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "bf42eb131d1a128773d749b9e5028201",
"identityHash": "fee48b09836f856f36971192954afe4e",
"entities": [
{
"tableName": "Song",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `artistsText` TEXT, `durationText` TEXT, `thumbnailUrl` TEXT, `dateModified` INTEGER, `likedAt` INTEGER, `totalPlayTimeMs` INTEGER NOT NULL, `loudnessBoost` REAL, `blacklisted` INTEGER NOT NULL DEFAULT false, `explicit` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `artistsText` TEXT, `durationText` TEXT, `thumbnailUrl` TEXT, `dateModified` INTEGER, `likedAt` INTEGER, `totalPlayTimeMs` INTEGER NOT NULL, `loudnessBoost` REAL, `blacklisted` INTEGER NOT NULL DEFAULT false, `explicit` INTEGER NOT NULL DEFAULT false, `path` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
Expand Down Expand Up @@ -75,6 +75,12 @@
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "false"
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
Expand Down Expand Up @@ -499,7 +505,7 @@
},
{
"tableName": "QueuedSong",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `position` INTEGER, `id` TEXT NOT NULL, `title` TEXT NOT NULL, `artistsText` TEXT, `durationText` TEXT, `thumbnailUrl` TEXT, `dateModified` INTEGER, `likedAt` INTEGER, `totalPlayTimeMs` INTEGER NOT NULL, `loudnessBoost` REAL, `blacklisted` INTEGER NOT NULL DEFAULT false, `explicit` INTEGER NOT NULL DEFAULT false)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `position` INTEGER, `id` TEXT NOT NULL, `title` TEXT NOT NULL, `artistsText` TEXT, `durationText` TEXT, `thumbnailUrl` TEXT, `dateModified` INTEGER, `likedAt` INTEGER, `totalPlayTimeMs` INTEGER NOT NULL, `loudnessBoost` REAL, `blacklisted` INTEGER NOT NULL DEFAULT false, `explicit` INTEGER NOT NULL DEFAULT false, `path` TEXT)",
"fields": [
{
"fieldPath": "itemId",
Expand Down Expand Up @@ -580,6 +586,12 @@
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "false"
},
{
"fieldPath": "song.path",
"columnName": "path",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
Expand Down Expand Up @@ -827,7 +839,7 @@
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'bf42eb131d1a128773d749b9e5028201')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'fee48b09836f856f36971192954afe4e')"
]
}
}
12 changes: 9 additions & 3 deletions app/schemas/app.banafsh.android.TempDatabaseInitializer/1.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "8994deddaeccca486674a0e49f658136",
"identityHash": "42ac1752fee93f42d326dc079b31c883",
"entities": [
{
"tableName": "Song",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `artistsText` TEXT, `durationText` TEXT, `thumbnailUrl` TEXT, `dateModified` INTEGER, `likedAt` INTEGER, `totalPlayTimeMs` INTEGER NOT NULL, `loudnessBoost` REAL, `blacklisted` INTEGER NOT NULL DEFAULT false, `explicit` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`))",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `artistsText` TEXT, `durationText` TEXT, `thumbnailUrl` TEXT, `dateModified` INTEGER, `likedAt` INTEGER, `totalPlayTimeMs` INTEGER NOT NULL, `loudnessBoost` REAL, `blacklisted` INTEGER NOT NULL DEFAULT false, `explicit` INTEGER NOT NULL DEFAULT false, `path` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
Expand Down Expand Up @@ -75,6 +75,12 @@
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "false"
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
Expand All @@ -90,7 +96,7 @@
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8994deddaeccca486674a0e49f658136')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '42ac1752fee93f42d326dc079b31c883')"
]
}
}
5 changes: 5 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" />

<queries>
<intent>
<action android:name="android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL" />
Expand All @@ -47,6 +51,7 @@
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher_round"
android:label="${appName}"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="false"
android:theme="@style/Theme.Banafsh.NoActionBar"
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/kotlin/app/banafsh/android/models/Song.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ data class Song(
@ColumnInfo(defaultValue = "false")
val blacklisted: Boolean = false,
@ColumnInfo(defaultValue = "false")
val explicit: Boolean = false
val explicit: Boolean = false,
val path: String? = null
) {
fun toggleLike() = copy(likedAt = if (likedAt == null) System.currentTimeMillis() else null)
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.center
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.Dp
Expand All @@ -47,12 +48,15 @@ import androidx.media3.common.util.UnstableApi
import app.banafsh.android.Database
import app.banafsh.android.LocalPlayerServiceBinder
import app.banafsh.android.R
import app.banafsh.android.TempDatabase
import app.banafsh.android.lib.core.ui.LocalAppearance
import app.banafsh.android.lib.core.ui.utils.roundedShape
import app.banafsh.android.models.Song
import app.banafsh.android.query
import app.banafsh.android.queryTemp
import app.banafsh.android.service.isLocal
import app.banafsh.android.utils.center
import app.banafsh.android.utils.delete
import app.banafsh.android.utils.drawCircle
import app.banafsh.android.utils.medium
import app.banafsh.android.utils.semiBold
Expand Down Expand Up @@ -439,15 +443,25 @@ fun HideSongDialog(
modifier: Modifier = Modifier
) {
val binder = LocalPlayerServiceBinder.current
val context = LocalContext.current

ConfirmationDialog(
text = stringResource(R.string.confirm_hide_song),
text = if (song.isLocal) stringResource(R.string.confirm_delete_song)
else stringResource(R.string.confirm_hide_song),
onDismiss = onDismiss,
onConfirm = {
onConfirm()
query {
runCatching {
if (!song.isLocal) binder?.cache?.removeResource(song.id)
runCatching {
if (!song.isLocal) {
binder?.cache?.removeResource(song.id)
} else {
if (!delete(context, song)) return@runCatching
queryTemp {
TempDatabase.delete(song)
}
}

query {
Database.delete(song)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,8 @@ fun MediaItemMenu(
onHideFromDatabase?.let {
MenuEntry(
icon = R.drawable.trash,
text = stringResource(R.string.hide),
text = if (mediaItem.isLocal) stringResource(R.string.delete)
else stringResource(R.string.hide),
onClick = onHideFromDatabase
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import app.banafsh.android.ui.components.themed.SecondaryTextButton
import app.banafsh.android.ui.screens.Route
import app.banafsh.android.utils.AudioMediaCursor
import app.banafsh.android.utils.hasPermission
import app.banafsh.android.utils.hasPermissions
import app.banafsh.android.utils.medium
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
Expand All @@ -59,22 +60,24 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.isActive

private val permission = if (isAtLeastAndroid13) Manifest.permission.READ_MEDIA_AUDIO
private val readPermission = if (isAtLeastAndroid13) Manifest.permission.READ_MEDIA_AUDIO
else Manifest.permission.READ_EXTERNAL_STORAGE

private val permissions = arrayOf(readPermission, Manifest.permission.WRITE_EXTERNAL_STORAGE)

@Route
@Composable
fun HomeLocalSongs(onSearchClick: () -> Unit) = with(OrderPreferences) {
val context = LocalContext.current
val (_, typography) = LocalAppearance.current

var hasPermission by remember(isCompositionLaunched()) {
mutableStateOf(context.applicationContext.hasPermission(permission))
mutableStateOf(context.applicationContext.hasPermissions(permissions))
}

val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = { hasPermission = it }
contract = ActivityResultContracts.RequestMultiplePermissions(),
onResult = { hasPermission = it.values.all { it } }
)

LaunchedEffect(hasPermission) {
Expand All @@ -99,7 +102,7 @@ fun HomeLocalSongs(onSearchClick: () -> Unit) = with(OrderPreferences) {
title = stringResource(R.string.songs)
)
else {
LaunchedEffect(Unit) { launcher.launch(permission) }
LaunchedEffect(Unit) { launcher.launch(permissions) }

Column(
modifier = Modifier.fillMaxSize(),
Expand Down Expand Up @@ -149,7 +152,8 @@ fun Context.musicFilesAsFlow(): StateFlow<List<Song>> = flow {
"$minutes:${seconds.toString().padStart(2, '0')}"
},
dateModified = dateModified,
thumbnailUrl = albumUri.toString()
thumbnailUrl = albumUri.toString(),
path = path
)
)
}
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/kotlin/app/banafsh/android/utils/Context.kt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ fun Context.findActivity(): Activity {
error("Should be called in the context of an Activity")
}

fun Context.hasPermissions(permissions: Array<String>) =
permissions.all { permission -> hasPermission(permission) }

fun Context.hasPermission(permission: String) = ContextCompat.checkSelfPermission(
applicationContext,
permission
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/kotlin/app/banafsh/android/utils/Cursor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import android.os.Bundle
import android.provider.MediaStore
import android.provider.MediaStore.Audio.Media.ALBUM_ID
import android.provider.MediaStore.Audio.Media.ARTIST
import android.provider.MediaStore.Audio.Media.DATA
import android.provider.MediaStore.Audio.Media.DATE_MODIFIED
import android.provider.MediaStore.Audio.Media.DURATION
import android.provider.MediaStore.Audio.Media.IS_MUSIC
Expand Down Expand Up @@ -182,6 +183,7 @@ class AudioMediaCursor(cursor: Cursor) : CursorDao(cursor) {
val duration by int(DURATION)
val dateModified by long(DATE_MODIFIED)
val artist by string(ARTIST)
val path by string(DATA)
private val albumId by long(ALBUM_ID)

val albumUri get() = ContentUris.withAppendedId(ALBUM_URI_BASE, albumId)
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/kotlin/app/banafsh/android/utils/Song.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package app.banafsh.android.utils

import android.content.ContentUris
import android.content.Context
import android.provider.MediaStore
import androidx.annotation.OptIn
import androidx.core.net.toUri
Expand All @@ -11,6 +12,7 @@ import androidx.media3.common.util.UnstableApi
import app.banafsh.android.models.Song
import app.banafsh.android.service.LOCAL_KEY_PREFIX
import app.banafsh.android.service.isLocal
import java.io.File

fun Song.getUri() = if (isLocal)
ContentUris.withAppendedId(
Expand Down Expand Up @@ -51,3 +53,14 @@ val Song.asMediaItem: MediaItem
.setUri(getUri())
.setCustomCacheKey(id)
.build()

fun delete(context: Context, song: Song): Boolean {
if (!song.path.isNullOrBlank()) {
val f = File(song.path)
if (f.delete()) {
context.contentResolver.delete(song.getUri(), null, null)
return true
}
}
return false
}
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@

<string name="hide_from_quick_picks">Hide from \"Quick picks\"</string>
<string name="confirm_hide_song">Do you really want to hide this song? Its playback time and cache will be wiped.\nThis action is irreversible.</string>
<string name="confirm_delete_song">Do you really want to delete this song? It is deleted from the local storage.\nThis action is irreversible.</string>
<string name="confirm_delete_playlist">Do you really want to delete this playlist?</string>
<string name="confirm_delete_piped_session">Do you really want to delete this Piped session?</string>

Expand Down

0 comments on commit 6dba4ae

Please sign in to comment.