mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-24 00:37:47 +00:00
Allow deletion of media through preview.
When viewing a media in the media preview, you can delete it by pressing a delete button on the action bar. It will then ask you to confirm your choice. If you confirm, it will delete the attachment from the database and from disk. If it was the only attachment for that message, the message itself will also be deleted.
This commit is contained in:
parent
a8cf5b8efa
commit
0c768a24e4
@ -13,4 +13,8 @@
|
||||
android:title="@string/media_preview__all_media_title"
|
||||
android:icon="@drawable/ic_photo_library_white_24dp"
|
||||
app:showAsAction="ifRoom"/>
|
||||
<item android:id="@+id/delete"
|
||||
android:title="@string/delete"
|
||||
android:icon="@drawable/ic_delete_white_24dp"
|
||||
app:showAsAction="ifRoom"/>
|
||||
</menu>
|
||||
|
@ -664,6 +664,8 @@
|
||||
<string name="MediaPreviewActivity_draft">Draft</string>
|
||||
<string name="MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied">Signal needs the Storage permission in order to save to external storage, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Storage\".</string>
|
||||
<string name="MediaPreviewActivity_unable_to_write_to_external_storage_without_permission">Unable to save to external storage without permissions</string>
|
||||
<string name="MediaPreviewActivity_media_delete_confirmation_title">Delete message?</string>
|
||||
<string name="MediaPreviewActivity_media_delete_confirmation_message">This will permanently delete this message.</string>
|
||||
|
||||
|
||||
<!-- MessageNotifier -->
|
||||
|
@ -20,6 +20,7 @@ import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
@ -34,6 +35,7 @@ import android.support.v4.content.Loader;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@ -46,9 +48,13 @@ import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.components.MediaView;
|
||||
import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
|
||||
import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
@ -251,6 +257,48 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private void deleteMedia() {
|
||||
MediaItem mediaItem = getCurrentMediaItem();
|
||||
if (mediaItem == null || mediaItem.attachment == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setIconAttribute(R.attr.dialog_alert_icon);
|
||||
builder.setTitle(R.string.MediaPreviewActivity_media_delete_confirmation_title);
|
||||
builder.setMessage(R.string.MediaPreviewActivity_media_delete_confirmation_message);
|
||||
builder.setCancelable(true);
|
||||
|
||||
builder.setPositiveButton(R.string.delete, (dialogInterface, which) -> {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
if (mediaItem.attachment == null) {
|
||||
return null;
|
||||
}
|
||||
Context context = MediaPreviewActivity.this.getApplicationContext();
|
||||
AttachmentId attachmentId = mediaItem.attachment.getAttachmentId();
|
||||
long mmsId = mediaItem.attachment.getMmsId();
|
||||
int attachmentCount = DatabaseFactory.getAttachmentDatabase(context)
|
||||
.getAttachmentsForMessage(mmsId)
|
||||
.size();
|
||||
|
||||
if (attachmentCount <= 1) {
|
||||
DatabaseFactory.getMmsDatabase(context).delete(mmsId);
|
||||
} else {
|
||||
DatabaseFactory.getAttachmentDatabase(context).deleteAttachment(attachmentId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
|
||||
finish();
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
@ -258,7 +306,11 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
menu.clear();
|
||||
MenuInflater inflater = this.getMenuInflater();
|
||||
inflater.inflate(R.menu.media_preview, menu);
|
||||
if (conversationRecipient == null) menu.findItem(R.id.media_preview__overview).setVisible(false);
|
||||
|
||||
if (!isMediaInDb()) {
|
||||
menu.findItem(R.id.media_preview__overview).setVisible(false);
|
||||
menu.findItem(R.id.delete).setVisible(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -271,12 +323,17 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
case R.id.media_preview__overview: showOverview(); return true;
|
||||
case R.id.media_preview__forward: forward(); return true;
|
||||
case R.id.save: saveToDisk(); return true;
|
||||
case R.id.delete: deleteMedia(); return true;
|
||||
case android.R.id.home: finish(); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isMediaInDb() {
|
||||
return conversationRecipient != null;
|
||||
}
|
||||
|
||||
private @Nullable MediaItem getCurrentMediaItem() {
|
||||
MediaItemAdapter adapter = (MediaItemAdapter)mediaPager.getAdapter();
|
||||
|
||||
@ -402,7 +459,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
|
||||
@Override
|
||||
public MediaItem getMediaItemFor(int position) {
|
||||
return new MediaItem(null, uri, mediaType, -1, true);
|
||||
return new MediaItem(null, null, uri, mediaType, -1, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -495,6 +552,7 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
if (mediaRecord.getAttachment().getDataUri() == null) throw new AssertionError();
|
||||
|
||||
return new MediaItem(address != null ? Recipient.from(context, address,true) : null,
|
||||
mediaRecord.getAttachment(),
|
||||
mediaRecord.getAttachment().getDataUri(),
|
||||
mediaRecord.getContentType(),
|
||||
mediaRecord.getDate(),
|
||||
@ -514,18 +572,26 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
}
|
||||
|
||||
private static class MediaItem {
|
||||
private final @Nullable Recipient recipient;
|
||||
private final @NonNull Uri uri;
|
||||
private final @NonNull String type;
|
||||
private final long date;
|
||||
private final boolean outgoing;
|
||||
private final @Nullable Recipient recipient;
|
||||
private final @Nullable DatabaseAttachment attachment;
|
||||
private final @NonNull Uri uri;
|
||||
private final @NonNull String type;
|
||||
private final long date;
|
||||
private final boolean outgoing;
|
||||
|
||||
private MediaItem(@Nullable Recipient recipient, @NonNull Uri uri, @NonNull String type, long date, boolean outgoing) {
|
||||
this.recipient = recipient;
|
||||
this.uri = uri;
|
||||
this.type = type;
|
||||
this.date = date;
|
||||
this.outgoing = outgoing;
|
||||
private MediaItem(@Nullable Recipient recipient,
|
||||
@Nullable DatabaseAttachment attachment,
|
||||
@NonNull Uri uri,
|
||||
@NonNull String type,
|
||||
long date,
|
||||
boolean outgoing)
|
||||
{
|
||||
this.recipient = recipient;
|
||||
this.attachment = attachment;
|
||||
this.uri = uri;
|
||||
this.type = type;
|
||||
this.date = date;
|
||||
this.outgoing = outgoing;
|
||||
}
|
||||
}
|
||||
|
||||
@ -533,5 +599,4 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
|
||||
MediaItem getMediaItemFor(int position);
|
||||
void pause(int position);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -234,16 +234,7 @@ public class AttachmentDatabase extends Database {
|
||||
new String[] {mmsId+""}, null, null, null);
|
||||
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
String data = cursor.getString(0);
|
||||
String thumbnail = cursor.getString(1);
|
||||
|
||||
if (!TextUtils.isEmpty(data)) {
|
||||
new File(data).delete();
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(thumbnail)) {
|
||||
new File(thumbnail).delete();
|
||||
}
|
||||
deleteAttachmentOnDisk(cursor.getString(0), cursor.getString(1));
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
@ -251,8 +242,34 @@ public class AttachmentDatabase extends Database {
|
||||
}
|
||||
|
||||
database.delete(TABLE_NAME, MMS_ID + " = ?", new String[] {mmsId + ""});
|
||||
notifyAttachmentListeners();
|
||||
}
|
||||
|
||||
public void deleteAttachment(@NonNull AttachmentId id) {
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
|
||||
try (Cursor cursor = database.query(TABLE_NAME,
|
||||
new String[]{DATA, THUMBNAIL},
|
||||
PART_ID_WHERE,
|
||||
id.toStrings(),
|
||||
null,
|
||||
null,
|
||||
null))
|
||||
{
|
||||
if (cursor == null || !cursor.moveToNext()) {
|
||||
Log.w(TAG, "Tried to delete an attachment, but it didn't exist.");
|
||||
return;
|
||||
}
|
||||
String data = cursor.getString(0);
|
||||
String thumbnail = cursor.getString(1);
|
||||
|
||||
database.delete(TABLE_NAME, PART_ID_WHERE, id.toStrings());
|
||||
deleteAttachmentOnDisk(data, thumbnail);
|
||||
notifyAttachmentListeners();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
void deleteAllAttachments() {
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
@ -264,6 +281,19 @@ public class AttachmentDatabase extends Database {
|
||||
for (File attachment : attachments) {
|
||||
attachment.delete();
|
||||
}
|
||||
|
||||
notifyAttachmentListeners();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
private void deleteAttachmentOnDisk(@Nullable String data, @Nullable String thumbnail) {
|
||||
if (!TextUtils.isEmpty(data)) {
|
||||
new File(data).delete();
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(thumbnail)) {
|
||||
new File(thumbnail).delete();
|
||||
}
|
||||
}
|
||||
|
||||
public void insertAttachmentsForPlaceholder(long mmsId, @NonNull AttachmentId attachmentId, @NonNull InputStream inputStream)
|
||||
|
@ -17,8 +17,10 @@
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
|
||||
@ -29,6 +31,7 @@ public abstract class Database {
|
||||
protected static final String ID_WHERE = "_id = ?";
|
||||
private static final String CONVERSATION_URI = "content://textsecure/thread/";
|
||||
private static final String CONVERSATION_LIST_URI = "content://textsecure/conversation-list";
|
||||
private static final String ATTACHMENT_URI = "content://textsecure/attachment/";
|
||||
|
||||
protected SQLCipherOpenHelper databaseHelper;
|
||||
protected final Context context;
|
||||
@ -59,6 +62,16 @@ public abstract class Database {
|
||||
cursor.setNotificationUri(context.getContentResolver(), Uri.parse(CONVERSATION_LIST_URI));
|
||||
}
|
||||
|
||||
protected void registerAttachmentListeners(@NonNull ContentObserver observer) {
|
||||
context.getContentResolver().registerContentObserver(Uri.parse(ATTACHMENT_URI),
|
||||
true,
|
||||
observer);
|
||||
}
|
||||
|
||||
protected void notifyAttachmentListeners() {
|
||||
context.getContentResolver().notifyChange(Uri.parse(ATTACHMENT_URI), null);
|
||||
}
|
||||
|
||||
public void reset(SQLCipherOpenHelper databaseHelper) {
|
||||
this.databaseHelper = databaseHelper;
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.ContentObservable;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@ -55,6 +57,14 @@ public class MediaDatabase extends Database {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public void subscribeToMediaChanges(@NonNull ContentObserver observer) {
|
||||
registerAttachmentListeners(observer);
|
||||
}
|
||||
|
||||
public void unsubscribeToMediaChanges(@NonNull ContentObserver observer) {
|
||||
context.getContentResolver().unregisterContentObserver(observer);
|
||||
}
|
||||
|
||||
public Cursor getDocumentMediaForThread(long threadId) {
|
||||
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||
Cursor cursor = database.rawQuery(DOCUMENT_MEDIA_QUERY, new String[]{threadId+""});
|
||||
@ -98,7 +108,7 @@ public class MediaDatabase extends Database {
|
||||
return new MediaRecord(attachment, address, date, outgoing);
|
||||
}
|
||||
|
||||
public Attachment getAttachment() {
|
||||
public DatabaseAttachment getAttachment() {
|
||||
return attachment;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.database.loaders;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.AsyncTaskLoader;
|
||||
@ -10,6 +11,7 @@ import com.annimon.stream.Stream;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.Database;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
@ -30,11 +32,13 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = BucketedThreadMediaLoader.class.getSimpleName();
|
||||
|
||||
private final Address address;
|
||||
private final Address address;
|
||||
private final ContentObserver observer;
|
||||
|
||||
public BucketedThreadMediaLoader(@NonNull Context context, @NonNull Address address) {
|
||||
super(context);
|
||||
this.address = address;
|
||||
this.address = address;
|
||||
this.observer = new ForceLoadContentObserver();
|
||||
|
||||
onContentChanged();
|
||||
}
|
||||
@ -51,11 +55,17 @@ public class BucketedThreadMediaLoader extends AsyncTaskLoader<BucketedThreadMed
|
||||
cancelLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAbandon() {
|
||||
DatabaseFactory.getMediaDatabase(getContext()).unsubscribeToMediaChanges(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BucketedThreadMedia loadInBackground() {
|
||||
BucketedThreadMedia result = new BucketedThreadMedia(getContext());
|
||||
long threadId = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(Recipient.from(getContext(), address, true));
|
||||
|
||||
DatabaseFactory.getMediaDatabase(getContext()).subscribeToMediaChanges(observer);
|
||||
try (Cursor cursor = DatabaseFactory.getMediaDatabase(getContext()).getGalleryMediaForThread(threadId)) {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
result.add(MediaDatabase.MediaRecord.from(getContext(), cursor));
|
||||
|
Loading…
x
Reference in New Issue
Block a user