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 android.provider.MediaStore.Audio.Media
|
||||||
import androidx.core.database.getStringOrNull
|
import androidx.core.database.getStringOrNull
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.database.BlacklistDatabase
|
||||||
import org.oxycblt.auxio.logD
|
import org.oxycblt.auxio.logD
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,11 +23,16 @@ class MusicLoader(private val context: Context) {
|
||||||
|
|
||||||
private val resolver = context.contentResolver
|
private val resolver = context.contentResolver
|
||||||
|
|
||||||
|
private var selector = "${Media.IS_MUSIC}=1"
|
||||||
|
private var args = arrayOf<String>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Begin the loading process.
|
* Begin the loading process.
|
||||||
* Resulting models are pushed to [genres], [artists], [albums], and [songs].
|
* Resulting models are pushed to [genres], [artists], [albums], and [songs].
|
||||||
*/
|
*/
|
||||||
fun load() {
|
fun load() {
|
||||||
|
buildSelector()
|
||||||
|
|
||||||
loadGenres()
|
loadGenres()
|
||||||
loadAlbums()
|
loadAlbums()
|
||||||
loadSongs()
|
loadSongs()
|
||||||
|
@ -36,6 +42,17 @@ class MusicLoader(private val context: Context) {
|
||||||
linkGenres()
|
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() {
|
private fun loadGenres() {
|
||||||
logD("Starting genre search...")
|
logD("Starting genre search...")
|
||||||
|
|
||||||
|
@ -127,7 +144,7 @@ class MusicLoader(private val context: Context) {
|
||||||
Media.TRACK, // 4
|
Media.TRACK, // 4
|
||||||
Media.DURATION // 5
|
Media.DURATION // 5
|
||||||
),
|
),
|
||||||
"${Media.IS_MUSIC}=1", null,
|
selector, args,
|
||||||
Media.DEFAULT_SORT_ORDER
|
Media.DEFAULT_SORT_ORDER
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -222,7 +239,7 @@ class MusicLoader(private val context: Context) {
|
||||||
val songCursor = resolver.query(
|
val songCursor = resolver.query(
|
||||||
Genres.Members.getContentUri("external", genre.id),
|
Genres.Members.getContentUri("external", genre.id),
|
||||||
arrayOf(Genres.Members._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 ->
|
songCursor?.use { cursor ->
|
||||||
|
|
Loading…
Reference in a new issue