refactor
parent
c7d2d78619
commit
18817f2642
|
@ -13,34 +13,34 @@ class SystemImage {
|
|||
var height = 0L
|
||||
var width = 0L
|
||||
var size = 0L
|
||||
var mtime = 0L
|
||||
var dataPath = ""
|
||||
var isVideo = false
|
||||
|
||||
private var mCollection: Uri = IMAGE_URI
|
||||
var isVideo = false
|
||||
var videoDuration = 0L
|
||||
|
||||
val uri: Uri
|
||||
get() {
|
||||
return ContentUris.withAppendedId(mCollection, fileId)
|
||||
}
|
||||
|
||||
private var mCollection: Uri = IMAGE_URI
|
||||
|
||||
companion object {
|
||||
val IMAGE_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||
val VIDEO_URI = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
||||
|
||||
fun getByIds(ctx: Context, ids: List<Long>): List<SystemImage> {
|
||||
val images = getByIdsAndCollection(ctx, IMAGE_URI, ids)
|
||||
if (images.size == ids.size) return images
|
||||
return images + getByIdsAndCollection(ctx, VIDEO_URI, ids)
|
||||
}
|
||||
|
||||
fun getByIdsAndCollection(ctx: Context, collection: Uri, ids: List<Long>): List<SystemImage> {
|
||||
val selection = MediaStore.Images.Media._ID + " IN (" + ids.joinToString(",") + ")"
|
||||
|
||||
fun query(
|
||||
ctx: Context,
|
||||
collection: Uri,
|
||||
selection: String?,
|
||||
selectionArgs: Array<String>?,
|
||||
sortOrder: String?
|
||||
): List<SystemImage> {
|
||||
val list = mutableListOf<SystemImage>()
|
||||
|
||||
ctx.contentResolver.query(
|
||||
collection,
|
||||
arrayOf(
|
||||
// Base fields common for videos and images
|
||||
val projection = arrayListOf(
|
||||
MediaStore.Images.Media._ID,
|
||||
MediaStore.Images.Media.DISPLAY_NAME,
|
||||
MediaStore.Images.Media.MIME_TYPE,
|
||||
|
@ -48,14 +48,27 @@ class SystemImage {
|
|||
MediaStore.Images.Media.WIDTH,
|
||||
MediaStore.Images.Media.SIZE,
|
||||
MediaStore.Images.Media.DATE_TAKEN,
|
||||
MediaStore.Images.Media.DATE_MODIFIED,
|
||||
MediaStore.Images.Media.DATA
|
||||
),
|
||||
)
|
||||
|
||||
// Add video-specific fields
|
||||
if (collection == VIDEO_URI) {
|
||||
projection.add(MediaStore.Video.Media.DURATION)
|
||||
}
|
||||
|
||||
// Query content resolver
|
||||
ctx.contentResolver.query(
|
||||
collection,
|
||||
projection.toTypedArray(),
|
||||
selection,
|
||||
null,
|
||||
null
|
||||
selectionArgs,
|
||||
sortOrder
|
||||
).use { cursor ->
|
||||
while (cursor!!.moveToNext()) {
|
||||
val image = SystemImage()
|
||||
|
||||
// Common fields
|
||||
image.fileId = cursor.getLong(0)
|
||||
image.baseName = cursor.getString(1)
|
||||
image.mimeType = cursor.getString(2)
|
||||
|
@ -63,14 +76,29 @@ class SystemImage {
|
|||
image.width = cursor.getLong(4)
|
||||
image.size = cursor.getLong(5)
|
||||
image.dateTaken = cursor.getLong(6)
|
||||
image.dataPath = cursor.getString(7)
|
||||
image.isVideo = collection == VIDEO_URI
|
||||
image.mtime = cursor.getLong(7)
|
||||
image.dataPath = cursor.getString(8)
|
||||
image.mCollection = collection
|
||||
|
||||
// Video specific fields
|
||||
image.isVideo = collection == VIDEO_URI
|
||||
if (image.isVideo) {
|
||||
image.videoDuration = cursor.getLong(9)
|
||||
}
|
||||
|
||||
// Add to main list
|
||||
list.add(image)
|
||||
}
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
fun getByIds(ctx: Context, ids: List<Long>): List<SystemImage> {
|
||||
val selection = MediaStore.Images.Media._ID + " IN (" + ids.joinToString(",") + ")"
|
||||
val images = query(ctx, IMAGE_URI, selection, null, null)
|
||||
if (images.size == ids.size) return images
|
||||
return images + query(ctx, VIDEO_URI, selection, null, null)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ import android.content.Context
|
|||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
|
||||
class DbService(context: Context) : SQLiteOpenHelper(context, "memories", null, 25) {
|
||||
class DbService(context: Context) : SQLiteOpenHelper(context, "memories", null, 26) {
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
db.execSQL("""
|
||||
CREATE TABLE images (
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
package gallery.memories.service
|
||||
|
||||
class Fields {
|
||||
object Day {
|
||||
const val DAYID = Photo.DAYID
|
||||
const val COUNT = "count"
|
||||
}
|
||||
|
||||
object Photo {
|
||||
const val FILEID = "fileid"
|
||||
const val BASENAME = "basename"
|
||||
|
|
|
@ -47,7 +47,7 @@ class TimelineQuery(private val mCtx: AppCompatActivity) {
|
|||
@Throws(JSONException::class)
|
||||
fun getByDayId(dayId: Long): JSONArray {
|
||||
// Get list of images from DB
|
||||
val imageIds: MutableSet<Long?> = ArraySet()
|
||||
val imageIds: MutableSet<Long> = ArraySet()
|
||||
val datesTaken: MutableMap<Long, Long> = HashMap()
|
||||
val sql = "SELECT local_id, date_taken FROM images WHERE dayid = ?"
|
||||
mDb.rawQuery(sql, arrayOf(dayId.toString())).use { cursor ->
|
||||
|
@ -62,75 +62,26 @@ class TimelineQuery(private val mCtx: AppCompatActivity) {
|
|||
if (imageIds.size == 0) return JSONArray()
|
||||
|
||||
// Filter for given day
|
||||
val idColName = MediaStore.Images.Media._ID
|
||||
val imageIdsSl = TextUtils.join(",", imageIds)
|
||||
val selection = "$idColName IN ($imageIdsSl)"
|
||||
|
||||
// Make list of files
|
||||
val files = ArrayList<JSONObject?>()
|
||||
mCtx.contentResolver.query(
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
arrayOf(
|
||||
MediaStore.Images.Media._ID,
|
||||
MediaStore.Images.Media.DISPLAY_NAME,
|
||||
MediaStore.Images.Media.MIME_TYPE,
|
||||
MediaStore.Images.Media.HEIGHT,
|
||||
MediaStore.Images.Media.WIDTH,
|
||||
MediaStore.Images.Media.SIZE,
|
||||
MediaStore.Images.Media.DATE_MODIFIED
|
||||
),
|
||||
selection,
|
||||
null,
|
||||
null
|
||||
).use { cursor ->
|
||||
while (cursor?.moveToNext() == true) {
|
||||
val fileId = cursor.getLong(0)
|
||||
imageIds.remove(fileId)
|
||||
files.add(JSONObject()
|
||||
.put(Fields.Photo.FILEID, fileId)
|
||||
.put(Fields.Photo.BASENAME, cursor.getString(1))
|
||||
.put(Fields.Photo.MIMETYPE, cursor.getString(2))
|
||||
.put(Fields.Photo.HEIGHT, cursor.getLong(3))
|
||||
.put(Fields.Photo.WIDTH, cursor.getLong(4))
|
||||
.put(Fields.Photo.SIZE, cursor.getLong(5))
|
||||
.put(Fields.Photo.ETAG, java.lang.Long.toString(cursor.getLong(6)))
|
||||
.put(Fields.Photo.DATETAKEN, datesTaken[fileId])
|
||||
.put(Fields.Photo.DAYID, dayId))
|
||||
}
|
||||
}
|
||||
mCtx.contentResolver.query(
|
||||
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
|
||||
arrayOf(
|
||||
MediaStore.Video.Media._ID,
|
||||
MediaStore.Video.Media.DISPLAY_NAME,
|
||||
MediaStore.Video.Media.MIME_TYPE,
|
||||
MediaStore.Video.Media.HEIGHT,
|
||||
MediaStore.Video.Media.WIDTH,
|
||||
MediaStore.Video.Media.SIZE,
|
||||
MediaStore.Video.Media.DATE_MODIFIED,
|
||||
MediaStore.Video.Media.DURATION
|
||||
),
|
||||
selection,
|
||||
null,
|
||||
null
|
||||
).use { cursor ->
|
||||
while (cursor?.moveToNext() == true) {
|
||||
// Remove from list of ids
|
||||
val fileId = cursor.getLong(0)
|
||||
imageIds.remove(fileId)
|
||||
files.add(JSONObject()
|
||||
.put(Fields.Photo.FILEID, fileId)
|
||||
.put(Fields.Photo.BASENAME, cursor.getString(1))
|
||||
.put(Fields.Photo.MIMETYPE, cursor.getString(2))
|
||||
.put(Fields.Photo.HEIGHT, cursor.getLong(3))
|
||||
.put(Fields.Photo.WIDTH, cursor.getLong(4))
|
||||
.put(Fields.Photo.SIZE, cursor.getLong(5))
|
||||
.put(Fields.Photo.ETAG, java.lang.Long.toString(cursor.getLong(6)))
|
||||
.put(Fields.Photo.DATETAKEN, datesTaken[fileId])
|
||||
val photos = JSONArray()
|
||||
SystemImage.getByIds(mCtx, imageIds.toMutableList()).forEach { image ->
|
||||
val obj = JSONObject()
|
||||
.put(Fields.Photo.FILEID, image.fileId)
|
||||
.put(Fields.Photo.BASENAME, image.baseName)
|
||||
.put(Fields.Photo.MIMETYPE, image.mimeType)
|
||||
.put(Fields.Photo.HEIGHT, image.height)
|
||||
.put(Fields.Photo.WIDTH, image.width)
|
||||
.put(Fields.Photo.SIZE, image.size)
|
||||
.put(Fields.Photo.ETAG, image.mtime.toString())
|
||||
.put(Fields.Photo.DATETAKEN, datesTaken[image.fileId])
|
||||
.put(Fields.Photo.DAYID, dayId)
|
||||
.put(Fields.Photo.ISVIDEO, 1)
|
||||
.put(Fields.Photo.VIDEO_DURATION, cursor.getLong(7) / 1000))
|
||||
|
||||
if (image.isVideo) {
|
||||
obj.put(Fields.Photo.ISVIDEO, 1)
|
||||
.put(Fields.Photo.VIDEO_DURATION, image.videoDuration / 1000)
|
||||
}
|
||||
|
||||
photos.put(obj)
|
||||
imageIds.remove(image.fileId)
|
||||
}
|
||||
|
||||
// Remove files that were not found
|
||||
|
@ -139,8 +90,7 @@ class TimelineQuery(private val mCtx: AppCompatActivity) {
|
|||
mDb.execSQL("DELETE FROM images WHERE local_id IN ($delIds)")
|
||||
}
|
||||
|
||||
// Return JSON string of files
|
||||
return JSONArray(files)
|
||||
return photos
|
||||
}
|
||||
|
||||
@Throws(JSONException::class)
|
||||
|
@ -151,11 +101,9 @@ class TimelineQuery(private val mCtx: AppCompatActivity) {
|
|||
).use { cursor ->
|
||||
val days = JSONArray()
|
||||
while (cursor.moveToNext()) {
|
||||
val id = cursor.getLong(0)
|
||||
val count = cursor.getLong(1)
|
||||
days.put(JSONObject()
|
||||
.put("dayid", id)
|
||||
.put("count", count)
|
||||
.put(Fields.Day.DAYID, cursor.getLong(0))
|
||||
.put(Fields.Day.COUNT, cursor.getLong(1))
|
||||
)
|
||||
}
|
||||
return days
|
||||
|
@ -269,69 +217,22 @@ class TimelineQuery(private val mCtx: AppCompatActivity) {
|
|||
private fun fullSyncDb() {
|
||||
// Flag all images for removal
|
||||
mDb.execSQL("UPDATE images SET flag = 1")
|
||||
mCtx.contentResolver.query(
|
||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
arrayOf(
|
||||
MediaStore.Images.Media._ID,
|
||||
MediaStore.Images.Media.DISPLAY_NAME,
|
||||
MediaStore.Images.Media.DATE_TAKEN,
|
||||
MediaStore.Images.Media.DATE_MODIFIED,
|
||||
MediaStore.Images.Media.DATA
|
||||
),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).use { cursor ->
|
||||
while (cursor!!.moveToNext()) {
|
||||
insertItemDb(
|
||||
cursor.getLong(0),
|
||||
cursor.getString(1),
|
||||
cursor.getLong(2),
|
||||
cursor.getLong(3),
|
||||
cursor.getString(4),
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
mCtx.contentResolver.query(
|
||||
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
|
||||
arrayOf(
|
||||
MediaStore.Video.Media._ID,
|
||||
MediaStore.Video.Media.DISPLAY_NAME,
|
||||
MediaStore.Video.Media.DATE_TAKEN,
|
||||
MediaStore.Video.Media.DATE_MODIFIED,
|
||||
MediaStore.Video.Media.DATA
|
||||
),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).use { cursor ->
|
||||
while (cursor!!.moveToNext()) {
|
||||
insertItemDb(
|
||||
cursor.getLong(0),
|
||||
cursor.getString(1),
|
||||
cursor.getLong(2),
|
||||
cursor.getLong(3),
|
||||
cursor.getString(4),
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate all images and videos from system store
|
||||
val files =
|
||||
SystemImage.query(mCtx, SystemImage.IMAGE_URI, null, null, null) +
|
||||
SystemImage.query(mCtx, SystemImage.VIDEO_URI, null, null, null)
|
||||
files.forEach { insertItemDb(it) }
|
||||
|
||||
// Clean up stale files
|
||||
mDb.execSQL("DELETE FROM images WHERE flag = 1")
|
||||
}
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
private fun insertItemDb(
|
||||
id: Long,
|
||||
name: String,
|
||||
dateTaken: Long,
|
||||
mtime: Long,
|
||||
uri: String,
|
||||
isVideo: Boolean,
|
||||
) {
|
||||
var dateTaken = dateTaken
|
||||
private fun insertItemDb(image: SystemImage) {
|
||||
var dateTaken = image.dateTaken
|
||||
val id = image.fileId
|
||||
val name = image.baseName
|
||||
|
||||
// Check if file with local_id and mtime already exists
|
||||
mDb.rawQuery("SELECT id FROM images WHERE local_id = ?", arrayOf(id.toString())).use { c ->
|
||||
|
@ -344,9 +245,9 @@ class TimelineQuery(private val mCtx: AppCompatActivity) {
|
|||
}
|
||||
|
||||
// Get EXIF date using ExifInterface if image
|
||||
if (!isVideo) {
|
||||
if (!image.isVideo) {
|
||||
try {
|
||||
val exif = ExifInterface(uri)
|
||||
val exif = ExifInterface(image.dataPath)
|
||||
val exifDate = exif.getAttribute(ExifInterface.TAG_DATETIME)
|
||||
?: throw IOException()
|
||||
val sdf = SimpleDateFormat("yyyy:MM:dd HH:mm:ss")
|
||||
|
@ -361,7 +262,7 @@ class TimelineQuery(private val mCtx: AppCompatActivity) {
|
|||
}
|
||||
|
||||
// No way to get the actual local date, so just assume current timezone
|
||||
if (isVideo) {
|
||||
else { // !isVideo
|
||||
dateTaken += TimeZone.getDefault().getOffset(dateTaken).toLong()
|
||||
}
|
||||
|
||||
|
@ -372,7 +273,9 @@ 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, mtime, name, dateTaken, dayId))
|
||||
mDb.execSQL("INSERT OR IGNORE INTO images (local_id, mtime, basename, date_taken, dayid) VALUES (?, ?, ?, ?, ?)", arrayOf(
|
||||
id, image.mtime, name, dateTaken, dayId
|
||||
))
|
||||
mDb.setTransactionSuccessful()
|
||||
mDb.endTransaction()
|
||||
Log.v(TAG, "Inserted file to local DB: $id / $name / $dayId")
|
||||
|
|
Loading…
Reference in New Issue