From 22ed3b3cb000b9749cfa96895d2da1175cd83c4a Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Wed, 10 May 2023 12:47:26 -0700 Subject: [PATCH] Add single delete API --- .../main/java/gallery/memories/NativeX.java | 12 ++-- .../memories/service/TimelineQuery.java | 69 ++++++++++++++++++- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/gallery/memories/NativeX.java b/app/src/main/java/gallery/memories/NativeX.java index 71062b92..2dc3da18 100644 --- a/app/src/main/java/gallery/memories/NativeX.java +++ b/app/src/main/java/gallery/memories/NativeX.java @@ -2,7 +2,6 @@ package gallery.memories; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; -import android.app.Activity; import android.app.DownloadManager; import android.content.Context; import android.graphics.Color; @@ -16,6 +15,7 @@ import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; import android.webkit.WebView; +import androidx.appcompat.app.AppCompatActivity; import androidx.collection.ArrayMap; import java.io.ByteArrayInputStream; @@ -26,13 +26,13 @@ import gallery.memories.service.TimelineQuery; public class NativeX { public static final String TAG = "NativeX"; - Activity mActivity; + AppCompatActivity mActivity; WebView mWebView; protected ImageService mImageService; protected TimelineQuery mQuery; - public NativeX(Activity activity, WebView webView) { + public NativeX(AppCompatActivity activity, WebView webView) { mActivity = activity; mWebView = webView; mImageService = new ImageService(activity); @@ -112,8 +112,10 @@ public class NativeX { return makeResponse(mImageService.getPreview(Long.parseLong(parts[3])), "image/jpeg"); } else if (path.matches("^/image/full/\\d+$")) { return makeResponse(mImageService.getFull(Long.parseLong(parts[3])), "image/jpeg"); - } else if (path.matches("^/image/info/\\d+$")) { - return makeResponse(mQuery.getImageInfo(Long.parseLong(parts[3]))); + } else if (path.matches("^/api/image/info/\\d+$")) { + return makeResponse(mQuery.getImageInfo(Long.parseLong(parts[4]))); + } else if (path.matches("^/api/image/delete/\\d+$")) { + return makeResponse(mQuery.delete(Long.parseLong(parts[4]))); } else if (path.matches("^/api/days$")) { return makeResponse(mQuery.getDays()); } else if (path.matches("/api/days/\\d+$")) { diff --git a/app/src/main/java/gallery/memories/service/TimelineQuery.java b/app/src/main/java/gallery/memories/service/TimelineQuery.java index 0ab148cb..05696513 100644 --- a/app/src/main/java/gallery/memories/service/TimelineQuery.java +++ b/app/src/main/java/gallery/memories/service/TimelineQuery.java @@ -1,14 +1,22 @@ package gallery.memories.service; -import android.content.Context; +import android.app.Activity; +import android.app.PendingIntent; +import android.content.ContentUris; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.icu.text.SimpleDateFormat; import android.net.Uri; +import android.os.Build; import android.provider.MediaStore; import android.text.TextUtils; import android.util.Log; +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.IntentSenderRequest; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.appcompat.app.AppCompatActivity; import androidx.collection.ArraySet; import androidx.exifinterface.media.ExifInterface; @@ -19,19 +27,31 @@ import org.json.JSONObject; import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; public class TimelineQuery { final static String TAG = "TimelineQuery"; - Context mCtx; + AppCompatActivity mCtx; SQLiteDatabase mDb; - public TimelineQuery(Context context) { + boolean deleting = false; + ActivityResultLauncher deleteIntentLauncher; + ActivityResult deleteResult; + + public TimelineQuery(AppCompatActivity context) { mCtx = context; mDb = new DbService(context).getWritableDatabase(); + deleteIntentLauncher = mCtx.registerForActivityResult(new ActivityResultContracts.StartIntentSenderForResult(), result -> { + synchronized (deleteIntentLauncher) { + deleteResult = result; + deleteIntentLauncher.notify(); + } + }); + fullSyncDb(); } @@ -257,6 +277,46 @@ public class TimelineQuery { } } + public JSONObject delete(long id) throws Exception { + synchronized (this) { + if (deleting) { + throw new Exception("Already deleting another set of images"); + } + deleting = true; + } + + try { + // Delete file with media store + Uri collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // Delete with media store + Uri uri = ContentUris.withAppendedId(collection, id); + PendingIntent intent = MediaStore.createTrashRequest(mCtx.getContentResolver(), Collections.singletonList(uri), true); + deleteIntentLauncher.launch(new IntentSenderRequest.Builder(intent.getIntentSender()).build()); + + // Wait for response + synchronized (deleteIntentLauncher) { + deleteIntentLauncher.wait(); + } + + // Throw if canceled or failed + if (deleteResult.getResultCode() != Activity.RESULT_OK) { + throw new Exception("Delete canceled or failed"); + } + } else { + // Delete with media store + Uri uri = ContentUris.withAppendedId(collection, id); + mCtx.getContentResolver().delete(uri, null, null); + } + + return new JSONObject().put("message", "ok"); + } finally { + synchronized (this) { + deleting = false; + } + } + } + protected void fullSyncDb() { Uri collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; @@ -332,5 +392,8 @@ public class TimelineQuery { Log.v(TAG, "Inserted file to local DB: " + id + " / " + name + " / " + dayId); } } + + // Clean up stale files + mDb.execSQL("DELETE FROM images WHERE flag = 1"); } }