Add local folder config
parent
5355dc5b38
commit
39d137a81c
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue