Add option to hide save storage warning.

This commit is contained in:
Michelle Tang 2025-01-23 14:17:58 -05:00 committed by GitHub
parent 83af313305
commit f128df7d95
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 84 additions and 36 deletions

View file

@ -2381,7 +2381,7 @@ class ConversationFragment :
val attachments = SaveAttachmentUtil.getAttachmentsForRecord(record)
SaveAttachmentUtil.showWarningDialog(requireContext(), attachments.size) { _, _ ->
SaveAttachmentUtil.showWarningDialogIfNecessary(requireContext()) {
if (StorageUtil.canWriteToMediaStore()) {
performAttachmentSave(attachments)
} else {

View file

@ -31,6 +31,7 @@ public class UiHintValues extends SignalStoreValues {
private static final String HAS_SEEN_CHAT_FOLDERS_EDUCATION_SHEET = "uihints.has_seen_chat_folders_education_sheet";
private static final String HAS_SEEN_LINK_DEVICE_QR_EDUCATION_SHEET = "uihints.has_seen_link_device_qr_education_sheet";
private static final String HAS_SEEN_LINK_DEVICE_AUTH_SHEET = "uihints.has_seen_link_device_auth_sheet";
private static final String HAS_DISMISSED_SAVE_STORAGE_WARNING = "uihints.has_dismissed_save_storage_warning";
UiHintValues(@NonNull KeyValueStore store) {
super(store);
@ -236,4 +237,12 @@ public class UiHintValues extends SignalStoreValues {
public boolean hasSeenLinkDeviceAuthSheet() {
return getBoolean(HAS_SEEN_LINK_DEVICE_AUTH_SHEET, false);
}
public boolean hasDismissedSaveStorageWarning() {
return getBoolean(HAS_DISMISSED_SAVE_STORAGE_WARNING, false);
}
public void markDismissedSaveStorageWarning() {
putBoolean(HAS_DISMISSED_SAVE_STORAGE_WARNING, true);
}
}

View file

@ -16,11 +16,9 @@ import org.thoughtcrime.securesms.database.MediaTable;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.jobs.MultiDeviceDeleteSyncJob;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.AttachmentUtil;
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
import org.thoughtcrime.securesms.util.StorageUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
@ -46,13 +44,13 @@ final class MediaActions {
return;
}
SaveAttachmentTask.showWarningDialog(context, (dialogInterface, which) -> Permissions.with(fragment)
SaveAttachmentTask.showWarningDialogIfNecessary(context, () -> Permissions.with(fragment)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.ifNecessary()
.withPermanentDenialDialog(fragment.getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied))
.onAnyDenied(() -> Toast.makeText(context, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show())
.onAllGranted(() -> performSaveToDisk(context, mediaRecords, postExecute))
.execute(), mediaRecords.size());
.execute());
}
static void handleDeleteMedia(@NonNull Context context,

View file

@ -4,7 +4,6 @@ import android.Manifest
import android.annotation.SuppressLint
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
@ -558,10 +557,10 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v
}
private fun saveToDisk(mediaItem: MediaTable.MediaRecord) {
SaveAttachmentTask.showWarningDialog(requireContext()) { _: DialogInterface?, _: Int ->
SaveAttachmentTask.showWarningDialogIfNecessary(requireContext()) {
if (StorageUtil.canWriteToMediaStore()) {
performSaveToDisk(mediaItem)
return@showWarningDialog
return@showWarningDialogIfNecessary
}
Permissions.with(this)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)

View file

@ -653,7 +653,7 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
@Override
public void onSave() {
SaveAttachmentTask.showWarningDialog(requireContext(), (dialogInterface, i) -> {
SaveAttachmentTask.showWarningDialogIfNecessary(requireContext(), () -> {
if (StorageUtil.canWriteToMediaStore()) {
performSaveToDisk();
return;

View file

@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.util;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface.OnClickListener;
import android.database.Cursor;
import android.media.MediaScannerConnection;
import android.net.Uri;
@ -11,6 +10,7 @@ import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.webkit.MimeTypeMap;
import android.widget.CheckBox;
import android.widget.Toast;
import androidx.annotation.NonNull;
@ -24,6 +24,7 @@ import org.signal.core.util.StreamUtil;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.util.Pair;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
@ -434,20 +435,25 @@ public class SaveAttachmentTask extends ProgressDialogAsyncTask<SaveAttachmentTa
}
}
public static void showWarningDialog(Context context, OnClickListener onAcceptListener) {
showWarningDialog(context, onAcceptListener, 1);
public static void showWarningDialogIfNecessary(Context context, Runnable onSave) {
if (SignalStore.uiHints().hasDismissedSaveStorageWarning()) {
onSave.run();
} else {
new MaterialAlertDialogBuilder(context)
.setView(R.layout.dialog_save_attachment)
.setTitle(R.string.ConversationFragment__save_to_phone)
.setCancelable(true)
.setMessage(R.string.ConversationFragment__this_media_will_be_saved)
.setPositiveButton(R.string.save, ((dialog, i) -> {
CheckBox checkbox = ((AlertDialog) dialog).findViewById(R.id.checkbox);
if (checkbox.isChecked()) {
SignalStore.uiHints().markDismissedSaveStorageWarning();
}
onSave.run();
}))
.setNegativeButton(android.R.string.cancel, null)
.show();
}
public static void showWarningDialog(Context context, OnClickListener onAcceptListener, int count) {
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(context);
builder.setTitle(R.string.ConversationFragment_save_to_sd_card);
builder.setIcon(R.drawable.symbol_error_triangle_fill_24);
builder.setCancelable(true);
builder.setMessage(context.getResources().getQuantityString(R.plurals.ConversationFragment_saving_n_media_to_storage_warning,
count, count));
builder.setPositiveButton(R.string.yes, onAcceptListener);
builder.setNegativeButton(R.string.no, null);
builder.show();
}
}

View file

@ -9,7 +9,6 @@ import android.annotation.SuppressLint
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.content.DialogInterface.OnClickListener
import android.database.Cursor
import android.media.MediaScannerConnection
import android.net.Uri
@ -17,8 +16,10 @@ import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.webkit.MimeTypeMap
import android.widget.CheckBox
import android.widget.Toast
import androidx.annotation.WorkerThread
import androidx.appcompat.app.AlertDialog
import androidx.core.content.contentValuesOf
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.reactivex.rxjava3.core.Single
@ -28,6 +29,7 @@ import org.signal.core.util.orNull
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.mms.PartAuthority
import java.io.File
import java.io.FileOutputStream
@ -52,16 +54,26 @@ object SaveAttachmentUtil {
private val TAG = Log.tag(SaveAttachmentUtil::class.java)
fun showWarningDialog(context: Context, count: Int, onAcceptListener: OnClickListener) {
fun showWarningDialogIfNecessary(context: Context, onSave: () -> Unit) {
if (SignalStore.uiHints.hasDismissedSaveStorageWarning()) {
onSave()
} else {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.ConversationFragment_save_to_sd_card)
.setIcon(R.drawable.symbol_error_triangle_fill_24)
.setView(R.layout.dialog_save_attachment)
.setTitle(R.string.ConversationFragment__save_to_phone)
.setCancelable(true)
.setMessage(context.resources.getQuantityString(R.plurals.ConversationFragment_saving_n_media_to_storage_warning, count, count))
.setPositiveButton(R.string.yes, onAcceptListener)
.setNegativeButton(R.string.no, null)
.setMessage(R.string.ConversationFragment__this_media_will_be_saved)
.setPositiveButton(R.string.save) { dialog, _ ->
val checkbox = (dialog as AlertDialog).findViewById<CheckBox>(R.id.checkbox)!!
if (checkbox.isChecked) {
SignalStore.uiHints.markDismissedSaveStorageWarning()
}
onSave()
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}
}
fun getAttachmentsForRecord(record: MmsMessageRecord): Set<SaveAttachment> {
return record.slideDeck.slides

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ConversationFragment_dont_show_again"
style="@style/Signal.Text.BodyMedium"
android:textColor="@color/signal_colorOnSurfaceVariant"
android:layout_marginStart="24dp"
android:layout_marginTop="12dp" />
</FrameLayout>

View file

@ -586,6 +586,12 @@
<item quantity="one">Saving attachment to storage…</item>
<item quantity="other">Saving %1$d attachments to storage…</item>
</plurals>
<!-- Dialog title asking to save media to your phone's storage -->
<string name="ConversationFragment__save_to_phone">Save to phone?</string>
<!-- Dialog message explaining that media will be saved to your phone and can potentially be accessed by other phones. -->
<string name="ConversationFragment__this_media_will_be_saved">This media will be saved to your phone\'s storage. Other apps may be able to access it depending on your phone\'s permissions.</string>
<!-- Checkbox shown in dialog to not show the dialog again in future cases -->
<string name="ConversationFragment_dont_show_again">Don\'t show again</string>
<string name="ConversationFragment_pending">Pending…</string>
<!-- Describes how the message was sent when looking at a message detail. Previously, messages could be through other means like SMS -->
<string name="ConversationFragment_push">Data (Signal)</string>