Add blacklist functionality
Add the ability to block directories from indexing. Will add a UI interface for this later.
This commit is contained in:
parent
8de09cd880
commit
f5f67a0b76
2 changed files with 161 additions and 2 deletions
|
@ -0,0 +1,142 @@
|
|||
package org.oxycblt.auxio.database
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import org.oxycblt.auxio.logD
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Database for storing blacklisted paths.
|
||||
* Note that the paths stored here will not work with MediaStore unless you append a "%" at the
|
||||
* end.
|
||||
*/
|
||||
class BlacklistDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS $TABLE_NAME ($COLUMN_PATH TEXT NOT NULL)")
|
||||
}
|
||||
|
||||
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
db.execSQL("DROP TABLE IF EXISTS $TABLE_NAME")
|
||||
onCreate(db)
|
||||
}
|
||||
|
||||
override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
db.execSQL("DROP TABLE IF EXISTS $TABLE_NAME")
|
||||
onCreate(db)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a [File] to the the blacklist.
|
||||
* @return Whether this file has been added to the database or not.
|
||||
*/
|
||||
fun addPath(file: File): Boolean {
|
||||
val path = file.mediaStorePath
|
||||
|
||||
logD("Adding path $path to blacklist")
|
||||
|
||||
if (hasFile(path)) {
|
||||
logD("Path already exists. Ignoring.")
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
val database = writableDatabase
|
||||
database.beginTransaction()
|
||||
|
||||
try {
|
||||
val values = ContentValues(1)
|
||||
values.put(COLUMN_PATH, path)
|
||||
|
||||
database.insert(TABLE_NAME, null, values)
|
||||
database.setTransactionSuccessful()
|
||||
} finally {
|
||||
database.endTransaction()
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a [File] from this blacklist.
|
||||
*/
|
||||
fun removePath(file: File) {
|
||||
val database = writableDatabase
|
||||
val path = file.mediaStorePath
|
||||
|
||||
database.beginTransaction()
|
||||
database.delete(TABLE_NAME, "$COLUMN_PATH=?", arrayOf(path))
|
||||
database.setTransactionSuccessful()
|
||||
database.endTransaction()
|
||||
}
|
||||
|
||||
fun getPaths(): List<String> {
|
||||
val paths = mutableListOf<String>()
|
||||
|
||||
val pathsCursor = readableDatabase.query(
|
||||
TABLE_NAME, arrayOf(COLUMN_PATH), null, null, null, null, null
|
||||
)
|
||||
|
||||
pathsCursor?.use { cursor ->
|
||||
while (cursor.moveToNext()) {
|
||||
paths.add(cursor.getString(0))
|
||||
}
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
private fun hasFile(path: String): Boolean {
|
||||
val pathsCursor = readableDatabase.query(
|
||||
TABLE_NAME,
|
||||
arrayOf(COLUMN_PATH),
|
||||
"$COLUMN_PATH=?",
|
||||
arrayOf(path),
|
||||
null, null, null, null
|
||||
)
|
||||
|
||||
pathsCursor?.use { cursor ->
|
||||
return cursor.moveToFirst()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private val File.mediaStorePath: String get() {
|
||||
return try {
|
||||
canonicalPath
|
||||
} catch (e: IOException) {
|
||||
absolutePath
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val DB_VERSION = 1
|
||||
const val DB_NAME = "auxio_blacklist_database.db"
|
||||
|
||||
const val TABLE_NAME = "blacklist_dirs_table"
|
||||
const val COLUMN_PATH = "COLUMN_PATH"
|
||||
|
||||
@Volatile
|
||||
private var INSTANCE: BlacklistDatabase? = null
|
||||
|
||||
/**
|
||||
* Get/Instantiate the single instance of [PlaybackStateDatabase].
|
||||
*/
|
||||
fun getInstance(context: Context): BlacklistDatabase {
|
||||
val currentInstance = INSTANCE
|
||||
|
||||
if (currentInstance != null) {
|
||||
return currentInstance
|
||||
}
|
||||
|
||||
synchronized(this) {
|
||||
val newInstance = BlacklistDatabase(context.applicationContext)
|
||||
INSTANCE = newInstance
|
||||
return newInstance
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import android.provider.MediaStore.Audio.Genres
|
|||
import android.provider.MediaStore.Audio.Media
|
||||
import androidx.core.database.getStringOrNull
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.database.BlacklistDatabase
|
||||
import org.oxycblt.auxio.logD
|
||||
|
||||
/**
|
||||
|
@ -22,11 +23,16 @@ class MusicLoader(private val context: Context) {
|
|||
|
||||
private val resolver = context.contentResolver
|
||||
|
||||
private var selector = "${Media.IS_MUSIC}=1"
|
||||
private var args = arrayOf<String>()
|
||||
|
||||
/**
|
||||
* Begin the loading process.
|
||||
* Resulting models are pushed to [genres], [artists], [albums], and [songs].
|
||||
*/
|
||||
fun load() {
|
||||
buildSelector()
|
||||
|
||||
loadGenres()
|
||||
loadAlbums()
|
||||
loadSongs()
|
||||
|
@ -36,6 +42,17 @@ class MusicLoader(private val context: Context) {
|
|||
linkGenres()
|
||||
}
|
||||
|
||||
private fun buildSelector() {
|
||||
val blacklistDatabase = BlacklistDatabase.getInstance(context)
|
||||
|
||||
val paths = blacklistDatabase.getPaths()
|
||||
|
||||
for (path in paths) {
|
||||
selector += " AND ${Media.DATA} NOT LIKE ?"
|
||||
args += "$path%" // Append % so that the selector properly detects children
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadGenres() {
|
||||
logD("Starting genre search...")
|
||||
|
||||
|
@ -127,7 +144,7 @@ class MusicLoader(private val context: Context) {
|
|||
Media.TRACK, // 4
|
||||
Media.DURATION // 5
|
||||
),
|
||||
"${Media.IS_MUSIC}=1", null,
|
||||
selector, args,
|
||||
Media.DEFAULT_SORT_ORDER
|
||||
)
|
||||
|
||||
|
@ -222,7 +239,7 @@ class MusicLoader(private val context: Context) {
|
|||
val songCursor = resolver.query(
|
||||
Genres.Members.getContentUri("external", genre.id),
|
||||
arrayOf(Genres.Members._ID),
|
||||
null, null, null
|
||||
null, null, null // Dont even bother selecting here as useless iters are less expensive than IO
|
||||
)
|
||||
|
||||
songCursor?.use { cursor ->
|
||||
|
|
Loading…
Reference in a new issue