Switch to room
parent
ec8c125b0e
commit
6dfe308268
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="11" />
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,17 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetDropDown">
|
||||
<runningDeviceTargetSelectedWithDropDown>
|
||||
<Target>
|
||||
<type value="RUNNING_DEVICE_TARGET" />
|
||||
<deviceKey>
|
||||
<Key>
|
||||
<type value="VIRTUAL_DEVICE_PATH" />
|
||||
<value value="C:\Users\varun\.android\avd\Pixel_6_API_33_2.avd" />
|
||||
</Key>
|
||||
</deviceKey>
|
||||
</Target>
|
||||
</runningDeviceTargetSelectedWithDropDown>
|
||||
<targetSelectedWithDropDown>
|
||||
<Target>
|
||||
<type value="QUICK_BOOT_TARGET" />
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="jbr-17" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.9.0" />
|
||||
</component>
|
||||
</project>
|
|
@ -6,7 +6,7 @@
|
|||
</list>
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'com.google.devtools.ksp' version '1.9.0-1.0.13'
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -22,28 +23,35 @@ android {
|
|||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
}
|
||||
|
||||
def mediaVersion = "1.0.1"
|
||||
|
||||
dependencies {
|
||||
def media_version = "1.1.1"
|
||||
def room_version = "2.5.2"
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.10.1'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.6.0'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.6.0'
|
||||
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.6'
|
||||
implementation "androidx.media3:media3-exoplayer:$mediaVersion"
|
||||
implementation "androidx.media3:media3-ui:$mediaVersion"
|
||||
implementation "androidx.media3:media3-exoplayer-hls:$mediaVersion"
|
||||
implementation "androidx.media3:media3-exoplayer:$media_version"
|
||||
implementation "androidx.media3:media3-ui:$media_version"
|
||||
implementation "androidx.media3:media3-exoplayer-hls:$media_version"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
annotationProcessor "androidx.room:room-compiler:$room_version"
|
||||
ksp "androidx.room:room-compiler:$room_version"
|
||||
|
||||
implementation "com.squareup.okhttp3:okhttp:4.10.0"
|
||||
implementation "io.github.g00fy2:versioncompare:1.5.0"
|
||||
}
|
|
@ -386,7 +386,7 @@ import gallery.memories.databinding.ActivityMainBinding
|
|||
fun refreshTimeline(force: Boolean = false) {
|
||||
runOnUiThread {
|
||||
// Check webview is loaded
|
||||
if (binding?.webview?.url == null) return@runOnUiThread
|
||||
if (binding.webview.url == null) return@runOnUiThread
|
||||
|
||||
// Schedule for resume if not active
|
||||
if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED) || force) {
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package gallery.memories.dao
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room.databaseBuilder
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import gallery.memories.R
|
||||
import gallery.memories.mapper.Photo
|
||||
|
||||
|
||||
@Database(entities = [Photo::class], version = 8)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun photoDao(): PhotoDao
|
||||
|
||||
companion object {
|
||||
private val DATABASE_NAME = "memories_room"
|
||||
@Volatile private var INSTANCE: AppDatabase? = null
|
||||
|
||||
fun get(context: Context): AppDatabase {
|
||||
if (INSTANCE == null) {
|
||||
synchronized(AppDatabase::class.java) {
|
||||
val ctx = context.applicationContext
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = databaseBuilder(ctx, AppDatabase::class.java, DATABASE_NAME)
|
||||
.fallbackToDestructiveMigration()
|
||||
.addCallback(callbacks(ctx))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE!!
|
||||
}
|
||||
|
||||
private fun callbacks(ctx: Context): Callback {
|
||||
return object : Callback() {
|
||||
override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
|
||||
super.onDestructiveMigration(db)
|
||||
|
||||
// retrigger synchronization whenever database is destructed
|
||||
ctx.getSharedPreferences(ctx.getString(R.string.preferences_key), 0).edit()
|
||||
.remove(ctx.getString(R.string.preferences_last_sync_time))
|
||||
.apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package gallery.memories.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import gallery.memories.mapper.Bucket
|
||||
import gallery.memories.mapper.Day
|
||||
import gallery.memories.mapper.Photo
|
||||
|
||||
@Dao
|
||||
interface PhotoDao {
|
||||
@Query("SELECT 1")
|
||||
fun ping(): Int
|
||||
|
||||
@Query("SELECT * FROM photos WHERE dayid=:dayId AND bucket_id IN (:buckets)")
|
||||
fun getPhotosByDay(dayId: Long, buckets: List<String>): List<Photo>
|
||||
|
||||
@Query("SELECT * FROM photos WHERE local_id IN (:fileIds)")
|
||||
fun getPhotosByFileIds(fileIds: List<Long>): List<Photo>
|
||||
|
||||
@Query("SELECT * FROM photos WHERE auid IN (:auids)")
|
||||
fun getPhotosByAUIDs(auids: List<Long>): List<Photo>
|
||||
|
||||
@Query("SELECT dayid, COUNT(local_id) AS count FROM photos WHERE bucket_id IN (:bucketIds) GROUP BY dayid")
|
||||
fun getDays(bucketIds: List<String>): List<Day>
|
||||
|
||||
@Query("DELETE FROM photos WHERE local_id IN (:fileIds)")
|
||||
fun deleteFileIds(fileIds: List<Long>)
|
||||
|
||||
@Query("UPDATE photos SET flag=1")
|
||||
fun flagAll()
|
||||
|
||||
@Query("UPDATE photos SET flag=0 WHERE local_id=:fileId")
|
||||
fun unflag(fileId: Long)
|
||||
|
||||
@Query("DELETE FROM photos WHERE flag=1")
|
||||
fun deleteFlagged()
|
||||
|
||||
@Insert
|
||||
fun insert(vararg photos: Photo)
|
||||
|
||||
@Query("SELECT bucket_id, bucket_name FROM photos GROUP BY bucket_id")
|
||||
fun getBuckets(): List<Bucket>
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package gallery.memories.mapper
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
|
||||
data class Bucket (
|
||||
@ColumnInfo(name="bucket_id") val id: String,
|
||||
@ColumnInfo(name="bucket_name") val name: String,
|
||||
)
|
|
@ -1,37 +1,8 @@
|
|||
package gallery.memories.mapper
|
||||
|
||||
import android.database.Cursor
|
||||
import org.json.JSONObject
|
||||
import androidx.room.ColumnInfo
|
||||
|
||||
class Day {
|
||||
val dayId: Long
|
||||
val count: Long
|
||||
|
||||
companion object {
|
||||
val FIELDS get(): Array<String> {
|
||||
return arrayOf(
|
||||
Photo.FIELD_DAY_ID,
|
||||
"COUNT(${Photo.FIELD_LOCAL_ID})"
|
||||
)
|
||||
}
|
||||
|
||||
fun unpack(cursor: Cursor): List<Day> {
|
||||
val days = mutableListOf<Day>()
|
||||
while (cursor.moveToNext()) {
|
||||
days.add(Day(cursor))
|
||||
}
|
||||
return days
|
||||
}
|
||||
}
|
||||
|
||||
constructor(cursor: Cursor) {
|
||||
dayId = cursor.getLong(0)
|
||||
count = cursor.getLong(1)
|
||||
}
|
||||
|
||||
val json get(): JSONObject {
|
||||
return JSONObject()
|
||||
.put(Fields.Day.DAYID, dayId)
|
||||
.put(Fields.Day.COUNT, count)
|
||||
}
|
||||
}
|
||||
data class Day (
|
||||
@ColumnInfo(name="dayid") val dayId: Long,
|
||||
@ColumnInfo(name="count") val count: Long
|
||||
)
|
|
@ -1,80 +1,19 @@
|
|||
package gallery.memories.mapper
|
||||
|
||||
import android.database.Cursor
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
class Photo {
|
||||
val id: Long
|
||||
val localId: Long
|
||||
val auid: Long
|
||||
val mtime: Long
|
||||
val dateTaken: Long
|
||||
val dayId: Long
|
||||
val basename: String
|
||||
val bucketId: Long
|
||||
val bucketName: String
|
||||
val flag: Int
|
||||
|
||||
companion object {
|
||||
val FIELD_ID = "id"
|
||||
val FIELD_LOCAL_ID = "local_id"
|
||||
val FIELD_AUID = "auid"
|
||||
val FIELD_MTIME = "mtime"
|
||||
val FIELD_DATE_TAKEN = "date_taken"
|
||||
val FIELD_DAY_ID = "dayid"
|
||||
val FIELD_BASENAME = "basename"
|
||||
val FIELD_BUCKET_ID = "bucket_id"
|
||||
val FIELD_BUCKET_NAME = "bucket_name"
|
||||
val FIELD_FLAG = "flag"
|
||||
|
||||
val FIELDS get(): Array<String> {
|
||||
return arrayOf(
|
||||
FIELD_ID,
|
||||
FIELD_LOCAL_ID,
|
||||
FIELD_AUID,
|
||||
FIELD_MTIME,
|
||||
FIELD_DATE_TAKEN,
|
||||
FIELD_DAY_ID,
|
||||
FIELD_BASENAME,
|
||||
FIELD_BUCKET_ID,
|
||||
FIELD_BUCKET_NAME,
|
||||
FIELD_FLAG
|
||||
)
|
||||
}
|
||||
|
||||
val FIELDS_CREATE get(): String {
|
||||
return """
|
||||
$FIELD_ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
$FIELD_LOCAL_ID INTEGER,
|
||||
$FIELD_AUID INTEGER,
|
||||
$FIELD_MTIME INTEGER,
|
||||
$FIELD_DATE_TAKEN INTEGER,
|
||||
$FIELD_DAY_ID INTEGER,
|
||||
$FIELD_BASENAME TEXT,
|
||||
$FIELD_BUCKET_ID INTEGER,
|
||||
$FIELD_BUCKET_NAME TEXT,
|
||||
$FIELD_FLAG INTEGER
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
fun unpack(cursor: Cursor): List<Photo> {
|
||||
val photos = mutableListOf<Photo>()
|
||||
while (cursor.moveToNext()) {
|
||||
photos.add(Photo(cursor))
|
||||
}
|
||||
return photos
|
||||
}
|
||||
}
|
||||
|
||||
constructor(cursor: Cursor) {
|
||||
id = cursor.getLong(0)
|
||||
localId = cursor.getLong(1)
|
||||
auid = cursor.getLong(2)
|
||||
mtime = cursor.getLong(3)
|
||||
dateTaken = cursor.getLong(4)
|
||||
dayId = cursor.getLong(5)
|
||||
basename = cursor.getString(6)
|
||||
bucketId = cursor.getLong(7)
|
||||
bucketName = cursor.getString(8)
|
||||
flag = cursor.getInt(9)
|
||||
}
|
||||
}
|
||||
@Entity(tableName="photos")
|
||||
data class Photo (
|
||||
@PrimaryKey(autoGenerate = true) val id: Int? = null,
|
||||
@ColumnInfo(name="local_id") val localId: Long,
|
||||
@ColumnInfo(name="auid") val auid: Long,
|
||||
@ColumnInfo(name="mtime") val mtime: Long,
|
||||
@ColumnInfo(name="date_taken") val dateTaken: Long,
|
||||
@ColumnInfo(name="dayid") val dayId: Long,
|
||||
@ColumnInfo(name="basename") val baseName: String,
|
||||
@ColumnInfo(name="bucket_id") val bucketId: Long,
|
||||
@ColumnInfo(name="bucket_name") val bucketName: String,
|
||||
@ColumnInfo(name="flag") val flag: Int
|
||||
)
|
|
@ -190,4 +190,19 @@ class SystemImage {
|
|||
|
||||
return crc.value
|
||||
}
|
||||
|
||||
val photo get(): Photo {
|
||||
val dateCache = utcDate
|
||||
return Photo(
|
||||
localId = fileId,
|
||||
auid = auid,
|
||||
mtime = mtime,
|
||||
dateTaken = dateCache,
|
||||
dayId = dateCache / 86400,
|
||||
baseName = baseName,
|
||||
bucketId = bucketId,
|
||||
bucketName = bucketName,
|
||||
flag = 0,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -5,20 +5,21 @@ import gallery.memories.R
|
|||
|
||||
class ConfigService(private val mCtx: Context) {
|
||||
companion object {
|
||||
private var mEnabledBuckets: Set<String>? = null
|
||||
private var mEnabledBuckets: List<String>? = null
|
||||
}
|
||||
|
||||
var enabledBucketIds: Set<String>
|
||||
var enabledBucketIds: List<String>
|
||||
get() {
|
||||
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()
|
||||
.getStringSet(mCtx.getString(R.string.preferences_enabled_local_folders), null)?.toList()
|
||||
?: listOf()
|
||||
return mEnabledBuckets!!
|
||||
}
|
||||
set(value) {
|
||||
mEnabledBuckets = value
|
||||
mCtx.getSharedPreferences(mCtx.getString(R.string.preferences_key), 0).edit()
|
||||
.putStringSet(mCtx.getString(R.string.preferences_enabled_local_folders), value)
|
||||
.putStringSet(mCtx.getString(R.string.preferences_enabled_local_folders), value.toSet())
|
||||
.apply()
|
||||
}
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
package gallery.memories.service
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import gallery.memories.R
|
||||
import gallery.memories.mapper.Day
|
||||
import gallery.memories.mapper.Photo
|
||||
import gallery.memories.mapper.SystemImage
|
||||
|
||||
class DbService(val context: Context) : SQLiteOpenHelper(context, "memories", null, 45) {
|
||||
companion object {
|
||||
val MEMORIES = "memories"
|
||||
}
|
||||
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
db.execSQL("CREATE TABLE $MEMORIES (${Photo.FIELDS_CREATE})")
|
||||
|
||||
// Add index on local_id, dayid, and flag
|
||||
db.execSQL("CREATE INDEX images_local_id ON $MEMORIES (${Photo.FIELD_LOCAL_ID})")
|
||||
db.execSQL("CREATE INDEX images_auid ON $MEMORIES (${Photo.FIELD_AUID})")
|
||||
db.execSQL("CREATE INDEX images_dayid ON $MEMORIES (${Photo.FIELD_DAY_ID})")
|
||||
db.execSQL("CREATE INDEX images_flag ON $MEMORIES (${Photo.FIELD_FLAG})")
|
||||
db.execSQL("CREATE INDEX images_bucket ON $MEMORIES (${Photo.FIELD_BUCKET_ID})")
|
||||
db.execSQL("CREATE INDEX images_bucket_dayid ON $MEMORIES (${Photo.FIELD_BUCKET_ID}, ${Photo.FIELD_DAY_ID})")
|
||||
}
|
||||
|
||||
override fun onUpgrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
database.execSQL("DROP TABLE IF EXISTS $MEMORIES")
|
||||
|
||||
// Reset sync time
|
||||
context.getSharedPreferences(context.getString(R.string.preferences_key), 0).edit()
|
||||
.remove(context.getString(R.string.preferences_last_sync_time))
|
||||
.apply()
|
||||
|
||||
onCreate(database)
|
||||
}
|
||||
|
||||
fun initialize(): DbService {
|
||||
writableDatabase
|
||||
return this
|
||||
}
|
||||
|
||||
fun getPhotosByDay(dayId: Long, buckets: Set<String>): List<Photo> {
|
||||
if (buckets.isEmpty()) return emptyList()
|
||||
|
||||
val bs = buckets.joinToString(",")
|
||||
val where = "${Photo.FIELD_DAY_ID} = ? AND ${Photo.FIELD_BUCKET_ID} IN ($bs)"
|
||||
readableDatabase.query(MEMORIES, Photo.FIELDS, where, arrayOf(
|
||||
dayId.toString(),
|
||||
), null, null, null).use { cursor ->
|
||||
return Photo.unpack(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
fun getPhotosBy(field: String, vals: List<Long>): List<Photo> {
|
||||
if (vals.isEmpty()) return emptyList()
|
||||
|
||||
val vls = vals.joinToString(",")
|
||||
readableDatabase.query(MEMORIES, Photo.FIELDS, "$field IN ($vls)", null,
|
||||
null, null, null
|
||||
).use { cursor ->
|
||||
return Photo.unpack(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
fun getPhotosByFileIds(fileIds: List<Long>): List<Photo> {
|
||||
return getPhotosBy(Photo.FIELD_LOCAL_ID, fileIds)
|
||||
}
|
||||
|
||||
fun getPhotosByAUIDs(auids: List<Long>): List<Photo> {
|
||||
return getPhotosBy(Photo.FIELD_AUID, auids)
|
||||
}
|
||||
|
||||
fun getDays(bucketIds: Set<String>): List<Day> {
|
||||
if (bucketIds.isEmpty()) return emptyList()
|
||||
|
||||
val bs = bucketIds.joinToString(",")
|
||||
val where = "${Photo.FIELD_BUCKET_ID} IN ($bs)"
|
||||
readableDatabase.query(MEMORIES, Day.FIELDS, where, null,
|
||||
Photo.FIELD_DAY_ID, null, null
|
||||
).use { cursor ->
|
||||
return Day.unpack(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteFileIds(fileIds: List<Long>) {
|
||||
if (fileIds.isEmpty()) return
|
||||
|
||||
val ids = fileIds.joinToString(",")
|
||||
writableDatabase.delete(MEMORIES, "${Photo.FIELD_LOCAL_ID} IN ($ids)", null)
|
||||
}
|
||||
|
||||
fun flagAll() {
|
||||
writableDatabase.execSQL("UPDATE $MEMORIES SET ${Photo.FIELD_FLAG} = 1")
|
||||
}
|
||||
|
||||
fun unflag(fileId: Long) {
|
||||
writableDatabase.execSQL("UPDATE $MEMORIES SET ${Photo.FIELD_FLAG} = 0 WHERE ${Photo.FIELD_LOCAL_ID} = $fileId")
|
||||
}
|
||||
|
||||
fun deleteFlagged() {
|
||||
writableDatabase.delete(MEMORIES, "${Photo.FIELD_FLAG} = 1", null)
|
||||
}
|
||||
|
||||
fun insertImage(image: SystemImage) {
|
||||
val dateTaken = image.utcDate
|
||||
|
||||
ContentValues().apply {
|
||||
put(Photo.FIELD_LOCAL_ID, image.fileId)
|
||||
put(Photo.FIELD_MTIME, image.mtime)
|
||||
put(Photo.FIELD_DATE_TAKEN, dateTaken)
|
||||
put(Photo.FIELD_DAY_ID, dateTaken / 86400)
|
||||
put(Photo.FIELD_AUID, image.auid)
|
||||
put(Photo.FIELD_BASENAME, image.baseName)
|
||||
put(Photo.FIELD_BUCKET_ID, image.bucketId)
|
||||
put(Photo.FIELD_BUCKET_NAME, image.bucketName)
|
||||
}.let {
|
||||
writableDatabase.insert(MEMORIES, null, it)
|
||||
}
|
||||
}
|
||||
|
||||
fun getBuckets(): Map<String, String> {
|
||||
val ret = mutableMapOf<String, String>()
|
||||
|
||||
readableDatabase.query(MEMORIES, arrayOf(Photo.FIELD_BUCKET_ID, Photo.FIELD_BUCKET_NAME),
|
||||
null, null, Photo.FIELD_BUCKET_ID, null, null
|
||||
).use { cursor ->
|
||||
while (cursor.moveToNext()) {
|
||||
ret[cursor.getString(0)] = cursor.getString(1)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ import androidx.exifinterface.media.ExifInterface
|
|||
import androidx.media3.common.util.UnstableApi
|
||||
import gallery.memories.MainActivity
|
||||
import gallery.memories.R
|
||||
import gallery.memories.dao.AppDatabase
|
||||
import gallery.memories.mapper.Fields
|
||||
import gallery.memories.mapper.Response
|
||||
import gallery.memories.mapper.SystemImage
|
||||
|
@ -26,9 +27,12 @@ import java.time.Instant
|
|||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
@UnstableApi class TimelineQuery(private val mCtx: MainActivity) {
|
||||
private val mDbService = DbService(mCtx).initialize()
|
||||
private val mConfigService = ConfigService(mCtx)
|
||||
private val TAG = TimelineQuery::class.java.simpleName
|
||||
private val mConfigService = ConfigService(mCtx)
|
||||
|
||||
// Database
|
||||
private val mDb = AppDatabase.get(mCtx)
|
||||
private val mPhotoDao = mDb.photoDao()
|
||||
|
||||
// Photo deletion events
|
||||
var deleting = false
|
||||
|
@ -50,6 +54,7 @@ import java.util.concurrent.CountDownLatch
|
|||
}
|
||||
|
||||
fun initialize() {
|
||||
mPhotoDao.ping()
|
||||
if (syncDeltaDb() > 0) {
|
||||
mCtx.refreshTimeline()
|
||||
}
|
||||
|
@ -103,8 +108,8 @@ import java.util.concurrent.CountDownLatch
|
|||
@Throws(JSONException::class)
|
||||
fun getByDayId(dayId: Long): JSONArray {
|
||||
// Get the photos for the day from DB
|
||||
val dbPhotos = mDbService.getPhotosByDay(dayId, mConfigService.enabledBucketIds)
|
||||
val fileIds = dbPhotos.map { it.localId }.toMutableList()
|
||||
val fileIds = mPhotoDao.getPhotosByDay(dayId, mConfigService.enabledBucketIds)
|
||||
.map { it.localId }.toMutableList()
|
||||
if (fileIds.isEmpty()) return JSONArray()
|
||||
|
||||
// Get latest metadata from system table
|
||||
|
@ -117,19 +122,23 @@ import java.util.concurrent.CountDownLatch
|
|||
}.let { JSONArray(it) }
|
||||
|
||||
// Remove files that were not found
|
||||
mDbService.deleteFileIds(fileIds)
|
||||
mPhotoDao.deleteFileIds(fileIds)
|
||||
|
||||
return photos
|
||||
}
|
||||
|
||||
@Throws(JSONException::class)
|
||||
fun getDays(): JSONArray {
|
||||
return mDbService.getDays(mConfigService.enabledBucketIds).map { day -> day.json }.let { JSONArray(it) }
|
||||
return mPhotoDao.getDays(mConfigService.enabledBucketIds).map {
|
||||
JSONObject()
|
||||
.put(Fields.Day.DAYID, it.dayId)
|
||||
.put(Fields.Day.COUNT, it.count)
|
||||
}.let { JSONArray(it) }
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun getImageInfo(id: Long): JSONObject {
|
||||
val photos = mDbService.getPhotosByFileIds(listOf(id))
|
||||
val photos = mPhotoDao.getPhotosByFileIds(listOf(id))
|
||||
if (photos.isEmpty()) throw Exception("File not found in database")
|
||||
|
||||
// Get image from system table
|
||||
|
@ -172,7 +181,7 @@ import java.util.concurrent.CountDownLatch
|
|||
|
||||
try {
|
||||
// Get list of file IDs
|
||||
val photos = mDbService.getPhotosByAUIDs(auids)
|
||||
val photos = mPhotoDao.getPhotosByAUIDs(auids)
|
||||
if (photos.isEmpty()) return Response.OK
|
||||
val fileIds = photos.map { it.localId }
|
||||
|
||||
|
@ -206,7 +215,7 @@ import java.util.concurrent.CountDownLatch
|
|||
}
|
||||
|
||||
// Delete from database
|
||||
mDbService.deleteFileIds(fileIds)
|
||||
mPhotoDao.deleteFileIds(fileIds)
|
||||
} finally {
|
||||
synchronized(this) { deleting = false }
|
||||
}
|
||||
|
@ -252,13 +261,13 @@ import java.util.concurrent.CountDownLatch
|
|||
|
||||
fun syncFullDb() {
|
||||
// Flag all images for removal
|
||||
mDbService.flagAll()
|
||||
mPhotoDao.flagAll()
|
||||
|
||||
// Sync all files, marking them in the process
|
||||
syncDb(0L)
|
||||
|
||||
// Clean up stale files
|
||||
mDbService.deleteFlagged()
|
||||
mPhotoDao.deleteFlagged()
|
||||
}
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
|
@ -267,38 +276,38 @@ import java.util.concurrent.CountDownLatch
|
|||
val baseName = image.baseName
|
||||
|
||||
// Check if file with local_id and mtime already exists
|
||||
val l = mDbService.getPhotosByFileIds(listOf(fileId))
|
||||
val l = mPhotoDao.getPhotosByFileIds(listOf(fileId))
|
||||
if (!l.isEmpty() && l[0].mtime == image.mtime) {
|
||||
// File already exists, remove flag
|
||||
mDbService.unflag(fileId)
|
||||
mPhotoDao.unflag(fileId)
|
||||
Log.v(TAG, "File already exists: $fileId / $baseName")
|
||||
return
|
||||
}
|
||||
|
||||
// Delete file with same local_id and insert new one
|
||||
mDbService.deleteFileIds(listOf(fileId))
|
||||
mDbService.insertImage(image)
|
||||
mPhotoDao.deleteFileIds(listOf(fileId))
|
||||
mPhotoDao.insert(image.photo)
|
||||
Log.v(TAG, "Inserted file to local DB: $fileId / $baseName")
|
||||
}
|
||||
|
||||
/** This is in timeline query because it calls the database service */
|
||||
var localFolders: JSONArray
|
||||
get() {
|
||||
return mDbService.getBuckets().map {
|
||||
return mPhotoDao.getBuckets().map {
|
||||
JSONObject()
|
||||
.put(Fields.Bucket.ID, it.key)
|
||||
.put(Fields.Bucket.NAME, it.value)
|
||||
.put(Fields.Bucket.ENABLED, mConfigService.enabledBucketIds.contains(it.key))
|
||||
.put(Fields.Bucket.ID, it.id)
|
||||
.put(Fields.Bucket.NAME, it.name)
|
||||
.put(Fields.Bucket.ENABLED, mConfigService.enabledBucketIds.contains(it.id))
|
||||
}.let { JSONArray(it) }
|
||||
}
|
||||
set(value) {
|
||||
val enabledSet = mutableSetOf<String>()
|
||||
val enabled = mutableListOf<String>()
|
||||
for (i in 0 until value.length()) {
|
||||
val obj = value.getJSONObject(i)
|
||||
if (obj.getBoolean(Fields.Bucket.ENABLED)) {
|
||||
enabledSet.add(obj.getString(Fields.Bucket.ID))
|
||||
enabled.add(obj.getString(Fields.Bucket.ID))
|
||||
}
|
||||
}
|
||||
mConfigService.enabledBucketIds = enabledSet
|
||||
mConfigService.enabledBucketIds = enabled
|
||||
}
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
buildscript {
|
||||
ext.kotlin_version = '1.8.10'
|
||||
ext.kotlin_version = '1.9.0'
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.android.application' version '7.3.1' apply false
|
||||
id 'com.android.library' version '7.3.1' apply false
|
||||
id 'com.android.application' version '8.1.0' apply false
|
||||
id 'com.android.library' version '8.1.0' apply false
|
||||
id 'org.jetbrains.kotlin.android' version "$kotlin_version" apply false
|
||||
}
|
|
@ -18,4 +18,6 @@ android.useAndroidX=true
|
|||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
android.nonTransitiveRClass=true
|
||||
android.defaults.buildfeatures.buildconfig=true
|
||||
android.nonFinalResIds=false
|
|
@ -1,6 +1,6 @@
|
|||
#Tue May 02 23:46:18 PDT 2023
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
Loading…
Reference in New Issue