Fix video deletion
parent
39f2af8dc3
commit
c7d2d78619
|
@ -0,0 +1,123 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
|
@ -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())
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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<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(",") + ")"
|
||||
|
||||
val list = mutableListOf<SystemImage>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue