mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 13:08:46 +00:00
Use AttachmentSaver to save image editor files to device storage.
This commit is contained in:
committed by
Cody Henthorne
parent
b1ff5dc5ef
commit
f0bb74a187
@@ -11,6 +11,8 @@ import android.widget.CheckBox
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import kotlinx.coroutines.rx3.rxCompletable
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.signal.core.ui.view.AlertDialogResult
|
||||
import org.signal.core.ui.view.awaitResult
|
||||
@@ -52,6 +54,8 @@ class AttachmentSaver(private val host: Host) {
|
||||
saveAttachments(attachments)
|
||||
}
|
||||
|
||||
fun saveAttachmentsRx(attachments: Set<SaveAttachment>): Completable = rxCompletable { saveAttachments(attachments) }
|
||||
|
||||
suspend fun saveAttachments(attachments: Set<SaveAttachment>) {
|
||||
if (checkIsSaveWarningAccepted(attachmentCount = attachments.size) == SaveToStorageWarningResult.ACCEPTED) {
|
||||
if (checkCanWriteToMediaStore() == RequestPermissionResult.GRANTED) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.thoughtcrime.securesms.scribbles;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -16,7 +15,6 @@ import android.view.HapticFeedbackConstants;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.ColorInt;
|
||||
@@ -34,7 +32,7 @@ import com.bumptech.glide.request.target.Target;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.signal.core.util.FontUtil;
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.concurrent.LifecycleDisposable;
|
||||
import org.signal.core.util.concurrent.SimpleTask;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.imageeditor.core.Bounds;
|
||||
@@ -50,6 +48,7 @@ import org.signal.imageeditor.core.renderers.MultiLineTextRenderer;
|
||||
import org.signal.libsignal.protocol.util.Pair;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.animation.ResizeAnimation;
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentSaver;
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies;
|
||||
import org.thoughtcrime.securesms.fonts.FontTypefaceProvider;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
@@ -66,8 +65,7 @@ import org.thoughtcrime.securesms.scribbles.stickers.FeatureSticker;
|
||||
import org.thoughtcrime.securesms.scribbles.stickers.TappableRenderer;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.ParcelUtil;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
import org.thoughtcrime.securesms.util.StorageUtil;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.ThrottledDebouncer;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
@@ -78,6 +76,9 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
|
||||
public final class ImageEditorFragment extends Fragment implements ImageEditorHudV2.EventListener,
|
||||
@@ -89,7 +90,7 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
|
||||
|
||||
public static final boolean CAN_RENDER_EMOJI = FontUtil.canRenderEmojiAtFontSize(1024);
|
||||
|
||||
private static final float PORTRAIT_ASPECT_RATIO = 9 / 16f;
|
||||
private static final float PORTRAIT_ASPECT_RATIO = 9 / 16f;
|
||||
|
||||
private static final String KEY_IMAGE_URI = "image_uri";
|
||||
private static final String KEY_MODE = "mode";
|
||||
@@ -99,6 +100,9 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
|
||||
private static final int DRAW_HUD_PROTECTION = ViewUtil.dpToPx(72);
|
||||
private static final int CROP_HUD_PROTECTION = ViewUtil.dpToPx(144);
|
||||
private static final int CONTROLS_PROTECTION = ViewUtil.dpToPx(74);
|
||||
|
||||
private final LifecycleDisposable lifecycleDisposable = new LifecycleDisposable();
|
||||
|
||||
private EditorModel restoredModel;
|
||||
|
||||
private Pair<Uri, FaceDetectionResult> cachedFaceDetection;
|
||||
@@ -224,6 +228,7 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
lifecycleDisposable.bindTo(getViewLifecycleOwner());
|
||||
controller.restoreState();
|
||||
|
||||
Mode mode = Mode.getByCode(requireArguments().getString(KEY_MODE));
|
||||
@@ -245,7 +250,7 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
|
||||
imageEditorView.addTextInputFilter(new RemoveEmojiTextFilter());
|
||||
}
|
||||
|
||||
int width = getResources().getDisplayMetrics().widthPixels;
|
||||
int width = getResources().getDisplayMetrics().widthPixels;
|
||||
int height = (int) ((16 / 9f) * width) - CONTROLS_PROTECTION;
|
||||
imageEditorView.setMinimumHeight(height);
|
||||
imageEditorView.requestLayout();
|
||||
@@ -654,20 +659,13 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
|
||||
|
||||
@Override
|
||||
public void onSave() {
|
||||
SaveAttachmentTask.showWarningDialogIfNecessary(requireContext(), 1, () -> {
|
||||
if (StorageUtil.canWriteToMediaStore()) {
|
||||
performSaveToDisk();
|
||||
return;
|
||||
}
|
||||
|
||||
Permissions.with(this)
|
||||
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
.ifNecessary()
|
||||
.withPermanentDenialDialog(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(requireContext(), R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show())
|
||||
.onAllGranted(this::performSaveToDisk)
|
||||
.execute();
|
||||
});
|
||||
lifecycleDisposable.add(
|
||||
Single.fromCallable(this::renderToSingleUseBlob)
|
||||
.subscribeOn(Schedulers.computation())
|
||||
.map(uri -> new SaveAttachmentUtil.SaveAttachment(uri, MediaUtil.IMAGE_JPEG, System.currentTimeMillis(), null))
|
||||
.flatMapCompletable(attachment -> new AttachmentSaver(this).saveAttachmentsRx(Collections.singleton(attachment)))
|
||||
.subscribe()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -754,10 +752,10 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
|
||||
resizeAnimation.cancel();
|
||||
}
|
||||
|
||||
int maxHeight = getHeightForOrientation(orientation);
|
||||
float aspectRatio = getAspectRatioForOrientation(orientation);
|
||||
int targetWidth = getWidthForOrientation(orientation);
|
||||
int targetHeight = (int) ((1 / aspectRatio) * targetWidth) - CONTROLS_PROTECTION;
|
||||
int maxHeight = getHeightForOrientation(orientation);
|
||||
float aspectRatio = getAspectRatioForOrientation(orientation);
|
||||
int targetWidth = getWidthForOrientation(orientation);
|
||||
int targetHeight = (int) ((1 / aspectRatio) * targetWidth) - CONTROLS_PROTECTION;
|
||||
|
||||
if (targetHeight > maxHeight) {
|
||||
targetHeight = maxHeight;
|
||||
@@ -798,14 +796,6 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
|
||||
return mode != ImageEditorHudV2.Mode.NONE;
|
||||
}
|
||||
|
||||
private void performSaveToDisk() {
|
||||
SimpleTask.run(this::renderToSingleUseBlob, uri -> {
|
||||
SaveAttachmentTask saveTask = new SaveAttachmentTask(requireContext());
|
||||
SaveAttachmentTask.Attachment attachment = new SaveAttachmentTask.Attachment(uri, MediaUtil.IMAGE_JPEG, System.currentTimeMillis(), null);
|
||||
saveTask.executeOnExecutor(SignalExecutors.BOUNDED, attachment);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("WrongThread")
|
||||
@WorkerThread
|
||||
public @NonNull Uri renderToSingleUseBlob() {
|
||||
@@ -967,7 +957,7 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
|
||||
imageEditorHud.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
editorElement.animatePartialFadeOut(imageEditorView::invalidate);
|
||||
});
|
||||
} else {
|
||||
@@ -998,7 +988,7 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
|
||||
if (imageEditorHud.getMode() != ImageEditorHudV2.Mode.TEXT) {
|
||||
imageEditorHud.setMode(ImageEditorHudV2.Mode.MOVE_TEXT);
|
||||
}
|
||||
} else if (editorElement != null && editorElement.getRenderer() instanceof UriGlideRenderer){
|
||||
} else if (editorElement != null && editorElement.getRenderer() instanceof UriGlideRenderer) {
|
||||
editorElement.animatePartialFadeIn(imageEditorView::invalidate);
|
||||
imageEditorHud.setMode(ImageEditorHudV2.Mode.MOVE_STICKER);
|
||||
}
|
||||
@@ -1076,6 +1066,12 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
public interface Controller {
|
||||
void onTouchEventsNeeded(boolean needed);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user