diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentSaver.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentSaver.kt index 882bfb8cae..21ab66700b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentSaver.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/AttachmentSaver.kt @@ -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): Completable = rxCompletable { saveAttachments(attachments) } + suspend fun saveAttachments(attachments: Set) { if (checkIsSaveWarningAccepted(attachmentCount = attachments.size) == SaveToStorageWarningResult.ACCEPTED) { if (checkCanWriteToMediaStore() == RequestPermissionResult.GRANTED) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java index ec750e7d3e..3d5ec5748e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java @@ -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 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);