From 39d137a81c56ee1885be84e53ccb9fe603f51670 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Sun, 21 May 2023 20:31:30 -0700 Subject: [PATCH] Add local folder config --- app/src/main/java/gallery/memories/NativeX.kt | 16 +++- .../gallery/memories/mapper/SystemImage.kt | 10 ++- .../gallery/memories/service/DbService.kt | 5 +- .../gallery/memories/service/TimelineQuery.kt | 78 +++++++++++++++++-- app/src/main/res/values/strings.xml | 1 + 5 files changed, 99 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/gallery/memories/NativeX.kt b/app/src/main/java/gallery/memories/NativeX.kt index f40b966a..8c6f4a57 100644 --- a/app/src/main/java/gallery/memories/NativeX.kt +++ b/app/src/main/java/gallery/memories/NativeX.kt @@ -33,9 +33,11 @@ import java.net.URLDecoder val IMAGE_PREVIEW = Regex("^/image/preview/\\d+$") val IMAGE_FULL = Regex("^/image/full/\\d+$") - val SHARE_URL = Regex("/api/share/url/.+$") - val SHARE_BLOB = Regex("/api/share/blob/.+$") - val SHARE_LOCAL = Regex("/api/share/local/\\d+$") + val SHARE_URL = Regex("^/api/share/url/.+$") + val SHARE_BLOB = Regex("^/api/share/blob/.+$") + val SHARE_LOCAL = Regex("^/api/share/local/\\d+$") + + val CONFIG_LOCAL_FOLDES = Regex("^/api/config/local-folders$") } init { @@ -178,6 +180,12 @@ import java.net.URLDecoder } } + @JavascriptInterface + fun configSetLocalFolders(json: String?) { + if (json == null) return; + query.configSetLocalFolders(json) + } + @Throws(Exception::class) private fun routerGet(path: String): WebResourceResponse { val parts = path.split("/").toTypedArray() @@ -199,6 +207,8 @@ import java.net.URLDecoder return makeResponse(dlService!!.shareBlobFromUrl(URLDecoder.decode(parts[4], "UTF-8"))) } else if (path.matches(API.SHARE_LOCAL)) { return makeResponse(dlService!!.shareLocal(parts[4].toLong())) + } else if (path.matches(API.CONFIG_LOCAL_FOLDES)) { + return makeResponse(query.getLocalFoldersConfig()) } else { throw Exception("Not Found") } diff --git a/app/src/main/java/gallery/memories/mapper/SystemImage.kt b/app/src/main/java/gallery/memories/mapper/SystemImage.kt index 616f9922..8c9cee89 100644 --- a/app/src/main/java/gallery/memories/mapper/SystemImage.kt +++ b/app/src/main/java/gallery/memories/mapper/SystemImage.kt @@ -15,6 +15,8 @@ class SystemImage { var size = 0L var mtime = 0L var dataPath = "" + var bucketId = 0L + var bucketName = "" var isVideo = false var videoDuration = 0L @@ -50,7 +52,9 @@ class SystemImage { MediaStore.Images.Media.ORIENTATION, MediaStore.Images.Media.DATE_TAKEN, 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 @@ -69,6 +73,8 @@ class SystemImage { val dateTakenColumn = projection.indexOf(MediaStore.Images.Media.DATE_TAKEN) val dateModifiedColumn = projection.indexOf(MediaStore.Images.Media.DATE_MODIFIED) 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 ctx.contentResolver.query( @@ -91,6 +97,8 @@ class SystemImage { image.dateTaken = cursor.getLong(dateTakenColumn) image.mtime = cursor.getLong(dateModifiedColumn) image.dataPath = cursor.getString(dataColumn) + image.bucketId = cursor.getLong(bucketIdColumn) + image.bucketName = cursor.getString(bucketNameColumn) image.mCollection = collection // Swap width/height if orientation is 90 or 270 diff --git a/app/src/main/java/gallery/memories/service/DbService.kt b/app/src/main/java/gallery/memories/service/DbService.kt index 244a100c..34fc070b 100644 --- a/app/src/main/java/gallery/memories/service/DbService.kt +++ b/app/src/main/java/gallery/memories/service/DbService.kt @@ -5,7 +5,7 @@ import android.database.sqlite.SQLiteOpenHelper import android.database.sqlite.SQLiteDatabase 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) { db.execSQL(""" CREATE TABLE images ( @@ -16,6 +16,8 @@ class DbService(val context: Context) : SQLiteOpenHelper(context, "memories", nu dayid INTEGER, exif_uid TEXT, basename TEXT, + bucket_id INTEGER, + bucket_name TEXT, 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_dayid ON images (dayid)") 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) { diff --git a/app/src/main/java/gallery/memories/service/TimelineQuery.kt b/app/src/main/java/gallery/memories/service/TimelineQuery.kt index 2164da34..78651897 100644 --- a/app/src/main/java/gallery/memories/service/TimelineQuery.kt +++ b/app/src/main/java/gallery/memories/service/TimelineQuery.kt @@ -35,6 +35,9 @@ class TimelineQuery(private val mCtx: AppCompatActivity) { var deleteIntentLauncher: ActivityResultLauncher var deleteCallback: ((ActivityResult?) -> Unit)? = null + // Caches + var mEnabledBuckets: Set? = null + init { // Register intent launcher for callback deleteIntentLauncher = mCtx.registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result: ActivityResult? -> @@ -46,11 +49,17 @@ class TimelineQuery(private val mCtx: AppCompatActivity) { @Throws(JSONException::class) fun getByDayId(dayId: Long): JSONArray { + // Filter for enabled buckets + val enabledBuckets = getEnabledBucketIds().joinToString(",") + // Get list of images from DB val imageIds: MutableSet = ArraySet() val datesTaken: MutableMap = HashMap() - val sql = "SELECT local_id, date_taken FROM images WHERE dayid = ?" - mDb.rawQuery(sql, arrayOf(dayId.toString())).use { cursor -> + mDb.rawQuery(""" + SELECT local_id, date_taken FROM images + WHERE dayid = ? + AND bucket_id IN ($enabledBuckets) + """, arrayOf(dayId.toString())).use { cursor -> while (cursor.moveToNext()) { val localId = cursor.getLong(0) datesTaken[localId] = cursor.getLong(1) @@ -95,8 +104,14 @@ class TimelineQuery(private val mCtx: AppCompatActivity) { @Throws(JSONException::class) fun getDays(): JSONArray { - mDb.rawQuery( - "SELECT dayid, COUNT(local_id) FROM images GROUP BY dayid", + // Filter for enabled buckets + 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 ).use { cursor -> val days = JSONArray() @@ -304,11 +319,62 @@ class TimelineQuery(private val mCtx: AppCompatActivity) { // Delete file with same local_id and insert new one mDb.beginTransaction() 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( - id, image.mtime, name, dateTaken, dayId + mDb.execSQL(""" + 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.endTransaction() Log.v(TAG, "Inserted file to local DB: $id / $name / $dayId") } + + fun getEnabledBucketIds(): Set { + 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() + 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() + } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eab26231..c56bd78f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,6 +7,7 @@ themeDark lastDbSyncTime hasMediaPermission + enabledLocalFolders "Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.76 Mobile Safari/537.36"