diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..7643783a --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,123 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..79ee123c --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/gallery/memories/MainActivity.kt b/app/src/main/java/gallery/memories/MainActivity.kt index 105ed09f..19fc569c 100644 --- a/app/src/main/java/gallery/memories/MainActivity.kt +++ b/app/src/main/java/gallery/memories/MainActivity.kt @@ -26,7 +26,7 @@ class MainActivity : AppCompatActivity() { } @SuppressLint("SetJavaScriptEnabled") - protected fun initializeWebView() { + private fun initializeWebView() { binding.webview.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { view.loadUrl(request.url.toString()) diff --git a/app/src/main/java/gallery/memories/NativeX.kt b/app/src/main/java/gallery/memories/NativeX.kt index ef3395bd..91ce7d70 100644 --- a/app/src/main/java/gallery/memories/NativeX.kt +++ b/app/src/main/java/gallery/memories/NativeX.kt @@ -48,14 +48,19 @@ class NativeX(private val mActivity: AppCompatActivity) { } fun handleRequest(request: WebResourceRequest): WebResourceResponse { - val path = request.url.path - val response: WebResourceResponse = try { - if (request.method == "GET") { - routerGet(path) - } else if (request.method == "OPTIONS") { - WebResourceResponse("text/plain", "UTF-8", ByteArrayInputStream("".toByteArray())) - } else { - throw Exception("Method Not Allowed") + val path = request.url.path ?: return makeErrorResponse() + + val response = try { + when (request.method) { + "GET" -> { + routerGet(path) + } + "OPTIONS" -> { + WebResourceResponse("text/plain", "UTF-8", ByteArrayInputStream("".toByteArray())) + } + else -> { + throw Exception("Method Not Allowed") + } } } catch (e: Exception) { Log.e(TAG, "handleRequest: ", e) @@ -89,12 +94,13 @@ class NativeX(private val mActivity: AppCompatActivity) { @JavascriptInterface fun downloadFromUrl(url: String?, filename: String?) { - mDlService.queue(url!!, filename!!) + if (url == null || filename == null) return; + mDlService.queue(url, filename) } @Throws(Exception::class) - private fun routerGet(path: String?): WebResourceResponse { - val parts = path!!.split("/").toTypedArray() + private fun routerGet(path: String): WebResourceResponse { + val parts = path.split("/").toTypedArray() if (path.matches(API.IMAGE_PREVIEW)) { return makeResponse(mImageService.getPreview(parts[3].toLong()), "image/jpeg") } else if (path.matches(API.IMAGE_FULL)) { diff --git a/app/src/main/java/gallery/memories/mapper/SystemImage.kt b/app/src/main/java/gallery/memories/mapper/SystemImage.kt new file mode 100644 index 00000000..81e28096 --- /dev/null +++ b/app/src/main/java/gallery/memories/mapper/SystemImage.kt @@ -0,0 +1,76 @@ +package gallery.memories.mapper + +import android.content.ContentUris +import android.content.Context +import android.net.Uri +import android.provider.MediaStore + +class SystemImage { + var fileId = 0L; + var baseName = "" + var mimeType = "" + var dateTaken = 0L + var height = 0L + var width = 0L + var size = 0L + var dataPath = "" + var isVideo = false + + private var mCollection: Uri = IMAGE_URI + + val uri: Uri + get() { + return ContentUris.withAppendedId(mCollection, fileId) + } + + 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): List { + 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): List { + val selection = MediaStore.Images.Media._ID + " IN (" + ids.joinToString(",") + ")" + + val list = mutableListOf() + + ctx.contentResolver.query( + collection, + 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_TAKEN, + MediaStore.Images.Media.DATA + ), + selection, + null, + null + ).use { cursor -> + while (cursor!!.moveToNext()) { + val image = SystemImage() + image.fileId = cursor.getLong(0) + image.baseName = cursor.getString(1) + image.mimeType = cursor.getString(2) + image.height = cursor.getLong(3) + 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.mCollection = collection + list.add(image) + } + } + + return list + } + } +} \ No newline at end of file diff --git a/app/src/main/java/gallery/memories/service/TimelineQuery.kt b/app/src/main/java/gallery/memories/service/TimelineQuery.kt index 3ec229dd..e9137f4d 100644 --- a/app/src/main/java/gallery/memories/service/TimelineQuery.kt +++ b/app/src/main/java/gallery/memories/service/TimelineQuery.kt @@ -2,11 +2,9 @@ package gallery.memories.service import android.annotation.SuppressLint import android.app.Activity -import android.content.ContentUris import android.database.sqlite.SQLiteDatabase import android.icu.text.SimpleDateFormat import android.icu.util.TimeZone -import android.net.Uri import android.os.Build import android.provider.MediaStore import android.text.TextUtils @@ -18,6 +16,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.collection.ArraySet import androidx.exifinterface.media.ExifInterface +import gallery.memories.mapper.SystemImage import org.json.JSONArray import org.json.JSONException import org.json.JSONObject @@ -165,68 +164,36 @@ class TimelineQuery(private val mCtx: AppCompatActivity) { @Throws(Exception::class) fun getImageInfo(id: Long): JSONObject { - val sql = "SELECT local_id, date_taken, dayid FROM images WHERE local_id = ?" + val sql = "SELECT dayid, date_taken FROM images WHERE local_id = ?" mDb.rawQuery(sql, arrayOf(id.toString())).use { cursor -> if (!cursor.moveToNext()) { throw Exception("Image not found") } - val localId = cursor.getLong(0) - val dateTaken = cursor.getLong(1) - val dayId = cursor.getLong(2) - - return getImageInfoForCollection( - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - localId, dateTaken, dayId) - ?: return getImageInfoForCollection( - MediaStore.Video.Media.EXTERNAL_CONTENT_URI, - localId, dateTaken, dayId) - ?: throw Exception("File not found in any collection") - } - } - - private fun getImageInfoForCollection( - collection: Uri, - localId: Long, - dateTaken: Long, - dayId: Long - ): JSONObject? { - val selection = MediaStore.Images.Media._ID + " = " + localId - mCtx.contentResolver.query( - collection, - 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.DATA - ), - selection, - null, - null - ).use { cursor -> - if (!cursor!!.moveToNext()) { - throw Exception("Image not found") + // Get image from system table + val imageList = SystemImage.getByIds(mCtx, arrayListOf(id)) + if (imageList.isEmpty()) { + throw Exception("File not found in any collection") } - // Create basic info + // Add EXIF to json object + val image = imageList[0]; + val dayId = cursor.getLong(0) + val dateTaken = cursor.getLong(1) + val obj = JSONObject() - .put(Fields.Photo.FILEID, cursor.getLong(0)) - .put(Fields.Photo.BASENAME, cursor.getString(1)) - .put(Fields.Photo.MIMETYPE, cursor.getString(2)) + .put(Fields.Photo.FILEID, image.fileId) + .put(Fields.Photo.BASENAME, image.baseName) + .put(Fields.Photo.MIMETYPE, image.mimeType) .put(Fields.Photo.DAYID, dayId) .put(Fields.Photo.DATETAKEN, dateTaken) - .put(Fields.Photo.HEIGHT, cursor.getLong(3)) - .put(Fields.Photo.WIDTH, cursor.getLong(4)) - .put(Fields.Photo.SIZE, cursor.getLong(5)) + .put(Fields.Photo.HEIGHT, image.height) + .put(Fields.Photo.WIDTH, image.width) + .put(Fields.Photo.SIZE, image.size) .put(Fields.Photo.PERMISSIONS, Fields.Perm.DELETE) - val uri = cursor.getString(6) - // Get EXIF data try { - val exif = ExifInterface(uri) + val exif = ExifInterface(image.dataPath) obj.put(Fields.Photo.EXIF, JSONObject() .put("Aperture", exif.getAttribute(ExifInterface.TAG_APERTURE_VALUE)) .put("FocalLength", exif.getAttribute(ExifInterface.TAG_FOCAL_LENGTH)) @@ -245,7 +212,7 @@ class TimelineQuery(private val mCtx: AppCompatActivity) { .put("Description", exif.getAttribute(ExifInterface.TAG_IMAGE_DESCRIPTION)) ) } catch (e: IOException) { - Log.e(TAG, "Error reading EXIF data for $uri") + Log.e(TAG, "Error reading EXIF data for $id") } return obj @@ -263,9 +230,7 @@ class TimelineQuery(private val mCtx: AppCompatActivity) { return try { // List of URIs - val uris = ids.map { - ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, it) - } + val uris = SystemImage.getByIds(mCtx, ids).map { it.uri } // Delete file with media store if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { @@ -301,7 +266,7 @@ class TimelineQuery(private val mCtx: AppCompatActivity) { } } - protected fun fullSyncDb() { + private fun fullSyncDb() { // Flag all images for removal mDb.execSQL("UPDATE images SET flag = 1") mCtx.contentResolver.query( @@ -381,7 +346,7 @@ class TimelineQuery(private val mCtx: AppCompatActivity) { // Get EXIF date using ExifInterface if image if (!isVideo) { try { - val exif = ExifInterface(uri!!) + val exif = ExifInterface(uri) val exifDate = exif.getAttribute(ExifInterface.TAG_DATETIME) ?: throw IOException() val sdf = SimpleDateFormat("yyyy:MM:dd HH:mm:ss")