Add local folder config

pull/653/merge
Varun Patil 2023-05-21 20:31:30 -07:00
parent 5355dc5b38
commit 39d137a81c
5 changed files with 99 additions and 11 deletions

View File

@ -33,9 +33,11 @@ import java.net.URLDecoder
val IMAGE_PREVIEW = Regex("^/image/preview/\\d+$") val IMAGE_PREVIEW = Regex("^/image/preview/\\d+$")
val IMAGE_FULL = Regex("^/image/full/\\d+$") val IMAGE_FULL = Regex("^/image/full/\\d+$")
val SHARE_URL = Regex("/api/share/url/.+$") val SHARE_URL = Regex("^/api/share/url/.+$")
val SHARE_BLOB = Regex("/api/share/blob/.+$") val SHARE_BLOB = Regex("^/api/share/blob/.+$")
val SHARE_LOCAL = Regex("/api/share/local/\\d+$") val SHARE_LOCAL = Regex("^/api/share/local/\\d+$")
val CONFIG_LOCAL_FOLDES = Regex("^/api/config/local-folders$")
} }
init { init {
@ -178,6 +180,12 @@ import java.net.URLDecoder
} }
} }
@JavascriptInterface
fun configSetLocalFolders(json: String?) {
if (json == null) return;
query.configSetLocalFolders(json)
}
@Throws(Exception::class) @Throws(Exception::class)
private fun routerGet(path: String): WebResourceResponse { private fun routerGet(path: String): WebResourceResponse {
val parts = path.split("/").toTypedArray() val parts = path.split("/").toTypedArray()
@ -199,6 +207,8 @@ import java.net.URLDecoder
return makeResponse(dlService!!.shareBlobFromUrl(URLDecoder.decode(parts[4], "UTF-8"))) return makeResponse(dlService!!.shareBlobFromUrl(URLDecoder.decode(parts[4], "UTF-8")))
} else if (path.matches(API.SHARE_LOCAL)) { } else if (path.matches(API.SHARE_LOCAL)) {
return makeResponse(dlService!!.shareLocal(parts[4].toLong())) return makeResponse(dlService!!.shareLocal(parts[4].toLong()))
} else if (path.matches(API.CONFIG_LOCAL_FOLDES)) {
return makeResponse(query.getLocalFoldersConfig())
} else { } else {
throw Exception("Not Found") throw Exception("Not Found")
} }

View File

@ -15,6 +15,8 @@ class SystemImage {
var size = 0L var size = 0L
var mtime = 0L var mtime = 0L
var dataPath = "" var dataPath = ""
var bucketId = 0L
var bucketName = ""
var isVideo = false var isVideo = false
var videoDuration = 0L var videoDuration = 0L
@ -50,7 +52,9 @@ class SystemImage {
MediaStore.Images.Media.ORIENTATION, MediaStore.Images.Media.ORIENTATION,
MediaStore.Images.Media.DATE_TAKEN, MediaStore.Images.Media.DATE_TAKEN,
MediaStore.Images.Media.DATE_MODIFIED, MediaStore.Images.Media.DATE_MODIFIED,
MediaStore.Images.Media.DATA MediaStore.Images.Media.DATA,
MediaStore.Images.Media.BUCKET_ID,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
) )
// Add video-specific fields // Add video-specific fields
@ -69,6 +73,8 @@ class SystemImage {
val dateTakenColumn = projection.indexOf(MediaStore.Images.Media.DATE_TAKEN) val dateTakenColumn = projection.indexOf(MediaStore.Images.Media.DATE_TAKEN)
val dateModifiedColumn = projection.indexOf(MediaStore.Images.Media.DATE_MODIFIED) val dateModifiedColumn = projection.indexOf(MediaStore.Images.Media.DATE_MODIFIED)
val dataColumn = projection.indexOf(MediaStore.Images.Media.DATA) val dataColumn = projection.indexOf(MediaStore.Images.Media.DATA)
val bucketIdColumn = projection.indexOf(MediaStore.Images.Media.BUCKET_ID)
val bucketNameColumn = projection.indexOf(MediaStore.Images.Media.BUCKET_DISPLAY_NAME)
// Query content resolver // Query content resolver
ctx.contentResolver.query( ctx.contentResolver.query(
@ -91,6 +97,8 @@ class SystemImage {
image.dateTaken = cursor.getLong(dateTakenColumn) image.dateTaken = cursor.getLong(dateTakenColumn)
image.mtime = cursor.getLong(dateModifiedColumn) image.mtime = cursor.getLong(dateModifiedColumn)
image.dataPath = cursor.getString(dataColumn) image.dataPath = cursor.getString(dataColumn)
image.bucketId = cursor.getLong(bucketIdColumn)
image.bucketName = cursor.getString(bucketNameColumn)
image.mCollection = collection image.mCollection = collection
// Swap width/height if orientation is 90 or 270 // Swap width/height if orientation is 90 or 270

View File

@ -5,7 +5,7 @@ import android.database.sqlite.SQLiteOpenHelper
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import gallery.memories.R import gallery.memories.R
class DbService(val context: Context) : SQLiteOpenHelper(context, "memories", null, 31) { class DbService(val context: Context) : SQLiteOpenHelper(context, "memories", null, 34) {
override fun onCreate(db: SQLiteDatabase) { override fun onCreate(db: SQLiteDatabase) {
db.execSQL(""" db.execSQL("""
CREATE TABLE images ( CREATE TABLE images (
@ -16,6 +16,8 @@ class DbService(val context: Context) : SQLiteOpenHelper(context, "memories", nu
dayid INTEGER, dayid INTEGER,
exif_uid TEXT, exif_uid TEXT,
basename TEXT, basename TEXT,
bucket_id INTEGER,
bucket_name TEXT,
flag INTEGER flag INTEGER
) )
""") """)
@ -24,6 +26,7 @@ class DbService(val context: Context) : SQLiteOpenHelper(context, "memories", nu
db.execSQL("CREATE INDEX images_local_id ON images (local_id)") db.execSQL("CREATE INDEX images_local_id ON images (local_id)")
db.execSQL("CREATE INDEX images_dayid ON images (dayid)") db.execSQL("CREATE INDEX images_dayid ON images (dayid)")
db.execSQL("CREATE INDEX images_flag ON images (flag)") db.execSQL("CREATE INDEX images_flag ON images (flag)")
db.execSQL("CREATE INDEX images_bucket ON images (bucket_id)")
} }
override fun onUpgrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) { override fun onUpgrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {

View File

@ -35,6 +35,9 @@ class TimelineQuery(private val mCtx: AppCompatActivity) {
var deleteIntentLauncher: ActivityResultLauncher<IntentSenderRequest> var deleteIntentLauncher: ActivityResultLauncher<IntentSenderRequest>
var deleteCallback: ((ActivityResult?) -> Unit)? = null var deleteCallback: ((ActivityResult?) -> Unit)? = null
// Caches
var mEnabledBuckets: Set<String>? = null
init { init {
// Register intent launcher for callback // Register intent launcher for callback
deleteIntentLauncher = mCtx.registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result: ActivityResult? -> deleteIntentLauncher = mCtx.registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result: ActivityResult? ->
@ -46,11 +49,17 @@ class TimelineQuery(private val mCtx: AppCompatActivity) {
@Throws(JSONException::class) @Throws(JSONException::class)
fun getByDayId(dayId: Long): JSONArray { fun getByDayId(dayId: Long): JSONArray {
// Filter for enabled buckets
val enabledBuckets = getEnabledBucketIds().joinToString(",")
// Get list of images from DB // Get list of images from DB
val imageIds: MutableSet<Long> = ArraySet() val imageIds: MutableSet<Long> = ArraySet()
val datesTaken: MutableMap<Long, Long> = HashMap() val datesTaken: MutableMap<Long, Long> = HashMap()
val sql = "SELECT local_id, date_taken FROM images WHERE dayid = ?" mDb.rawQuery("""
mDb.rawQuery(sql, arrayOf(dayId.toString())).use { cursor -> SELECT local_id, date_taken FROM images
WHERE dayid = ?
AND bucket_id IN ($enabledBuckets)
""", arrayOf(dayId.toString())).use { cursor ->
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
val localId = cursor.getLong(0) val localId = cursor.getLong(0)
datesTaken[localId] = cursor.getLong(1) datesTaken[localId] = cursor.getLong(1)
@ -95,8 +104,14 @@ class TimelineQuery(private val mCtx: AppCompatActivity) {
@Throws(JSONException::class) @Throws(JSONException::class)
fun getDays(): JSONArray { fun getDays(): JSONArray {
mDb.rawQuery( // Filter for enabled buckets
"SELECT dayid, COUNT(local_id) FROM images GROUP BY dayid", val enabledBuckets = getEnabledBucketIds().joinToString(",")
// Get this day's images
mDb.rawQuery("""
SELECT dayid, COUNT(local_id) FROM images
WHERE bucket_id IN ($enabledBuckets)
GROUP BY dayid""",
null null
).use { cursor -> ).use { cursor ->
val days = JSONArray() val days = JSONArray()
@ -304,11 +319,62 @@ class TimelineQuery(private val mCtx: AppCompatActivity) {
// Delete file with same local_id and insert new one // Delete file with same local_id and insert new one
mDb.beginTransaction() mDb.beginTransaction()
mDb.execSQL("DELETE FROM images WHERE local_id = ?", arrayOf(id)) mDb.execSQL("DELETE FROM images WHERE local_id = ?", arrayOf(id))
mDb.execSQL("INSERT OR IGNORE INTO images (local_id, mtime, basename, date_taken, dayid) VALUES (?, ?, ?, ?, ?)", arrayOf( mDb.execSQL("""
id, image.mtime, name, dateTaken, dayId INSERT OR IGNORE INTO images
(local_id, mtime, basename, date_taken, dayid, bucket_id, bucket_name)
VALUES (?, ?, ?, ?, ?, ?, ?)
""", arrayOf(
image.fileId,
image.mtime,
image.baseName,
dateTaken,
dayId,
image.bucketId,
image.bucketName
)) ))
mDb.setTransactionSuccessful() mDb.setTransactionSuccessful()
mDb.endTransaction() mDb.endTransaction()
Log.v(TAG, "Inserted file to local DB: $id / $name / $dayId") Log.v(TAG, "Inserted file to local DB: $id / $name / $dayId")
} }
fun getEnabledBucketIds(): Set<String> {
if (mEnabledBuckets != null) return mEnabledBuckets!!
mEnabledBuckets = mCtx.getSharedPreferences(mCtx.getString(R.string.preferences_key), 0)
.getStringSet(mCtx.getString(R.string.preferences_enabled_local_folders), null) ?: setOf()
return mEnabledBuckets!!
}
fun getLocalFoldersConfig(): JSONArray {
val array = JSONArray()
val enabledSet = getEnabledBucketIds()
val sql = "SELECT bucket_id, bucket_name FROM images GROUP BY bucket_id"
mDb.rawQuery(sql, emptyArray()).use { cursor ->
while (cursor.moveToNext()) {
val obj = JSONObject()
val id = cursor.getLong(0)
obj.put("id", id)
obj.put("name", cursor.getString(1))
obj.put("enabled", enabledSet.contains(id.toString()))
array.put(obj)
}
}
return array
}
fun configSetLocalFolders(json: String) {
val enabledSet = mutableSetOf<String>()
val array = JSONArray(json)
for (i in 0 until array.length()) {
val obj = array.getJSONObject(i)
if (obj.getBoolean("enabled")) {
enabledSet.add(obj.getLong("id").toString())
}
}
mEnabledBuckets = enabledSet
mCtx.getSharedPreferences(mCtx.getString(R.string.preferences_key), 0).edit()
.putStringSet(mCtx.getString(R.string.preferences_enabled_local_folders), enabledSet)
.apply()
}
} }

View File

@ -7,6 +7,7 @@
<string name="preferences_theme_dark">themeDark</string> <string name="preferences_theme_dark">themeDark</string>
<string name="preferences_last_sync_time">lastDbSyncTime</string> <string name="preferences_last_sync_time">lastDbSyncTime</string>
<string name="preferences_has_media_permission">hasMediaPermission</string> <string name="preferences_has_media_permission">hasMediaPermission</string>
<string name="preferences_enabled_local_folders">enabledLocalFolders</string>
<!-- https://www.whatismybrowser.com/guides/the-latest-user-agent/chrome --> <!-- https://www.whatismybrowser.com/guides/the-latest-user-agent/chrome -->
<string name="ua_chrome">"Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.76 Mobile Safari/537.36"</string> <string name="ua_chrome">"Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.76 Mobile Safari/537.36"</string>