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