diff --git a/res/drawable-hdpi/ic_download_32.png b/res/drawable-hdpi/ic_download_32.png new file mode 100644 index 0000000000..38e7786c2b Binary files /dev/null and b/res/drawable-hdpi/ic_download_32.png differ diff --git a/res/drawable-mdpi/ic_download_32.png b/res/drawable-mdpi/ic_download_32.png new file mode 100644 index 0000000000..072e14daa4 Binary files /dev/null and b/res/drawable-mdpi/ic_download_32.png differ diff --git a/res/drawable-xhdpi/ic_download_32.png b/res/drawable-xhdpi/ic_download_32.png new file mode 100644 index 0000000000..7013c3149e Binary files /dev/null and b/res/drawable-xhdpi/ic_download_32.png differ diff --git a/res/drawable-xxhdpi/ic_download_32.png b/res/drawable-xxhdpi/ic_download_32.png new file mode 100644 index 0000000000..c5c64063db Binary files /dev/null and b/res/drawable-xxhdpi/ic_download_32.png differ diff --git a/res/drawable-xxxhdpi/ic_download_32.png b/res/drawable-xxxhdpi/ic_download_32.png new file mode 100644 index 0000000000..d6972b48a8 Binary files /dev/null and b/res/drawable-xxxhdpi/ic_download_32.png differ diff --git a/res/layout/image_editor_hud.xml b/res/layout/image_editor_hud.xml index d78afa6686..4ebafacaca 100644 --- a/res/layout/image_editor_hud.xml +++ b/res/layout/image_editor_hud.xml @@ -22,6 +22,7 @@ android:layout_marginTop="8dp" android:layout_marginEnd="10dp" android:orientation="horizontal" + android:gravity="center_vertical" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent"> @@ -41,6 +42,14 @@ android:padding="8dp" android:src="@drawable/ic_undo_32" /> + + { dialog = new AlertDialog.Builder(new ContextThemeWrapper(MediaSendActivity.this, R.style.TextSecure_MediaSendProgressDialog)) - .setView(R.layout.progress_dialog) - .setCancelable(false) - .create(); + .setView(R.layout.progress_dialog) + .setCancelable(false) + .create(); dialog.show(); dialog.getWindow().setLayout(getResources().getDimensionPixelSize(R.dimen.mediasend_progress_dialog_size), - getResources().getDimensionPixelSize(R.dimen.mediasend_progress_dialog_size)); + getResources().getDimensionPixelSize(R.dimen.mediasend_progress_dialog_size)); }; Util.runOnMainDelayed(progressTimer, 250); } diff --git a/src/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java b/src/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java index fe7cb36d80..50ab3041ad 100644 --- a/src/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java +++ b/src/org/thoughtcrime/securesms/scribbles/ImageEditorFragment.java @@ -1,6 +1,8 @@ package org.thoughtcrime.securesms.scribbles; +import android.Manifest; import android.content.Intent; +import android.graphics.Bitmap; import android.graphics.Paint; import android.net.Uri; import android.os.Bundle; @@ -10,6 +12,7 @@ import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Toast; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.imageeditor.ColorableRenderer; @@ -22,9 +25,18 @@ import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.mediasend.MediaSendPageFragment; import org.thoughtcrime.securesms.mms.MediaConstraints; import org.thoughtcrime.securesms.mms.PushMediaConstraints; +import org.thoughtcrime.securesms.permissions.Permissions; +import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.scribbles.widget.VerticalSlideColorPicker; +import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.ParcelUtil; +import org.thoughtcrime.securesms.util.SaveAttachmentTask; import org.thoughtcrime.securesms.util.TextSecurePreferences; +import org.thoughtcrime.securesms.util.concurrent.SignalExecutors; +import org.thoughtcrime.securesms.util.concurrent.SimpleTask; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; import static android.app.Activity.RESULT_OK; @@ -287,6 +299,36 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu refreshUniqueColors(); } + @Override + public void onSave() { + SaveAttachmentTask.showWarningDialog(requireContext(), (dialogInterface, i) -> { + Permissions.with(this) + .request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_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(() -> { + SimpleTask.run(() -> { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + Bitmap image = imageEditorView.getModel().render(requireContext()); + + image.compress(Bitmap.CompressFormat.JPEG, 80, outputStream); + + return BlobProvider.getInstance() + .forData(outputStream.toByteArray()) + .withMimeType(MediaUtil.IMAGE_JPEG) + .createForSingleUseInMemory(); + + }, uri -> { + SaveAttachmentTask saveTask = new SaveAttachmentTask(requireContext()); + SaveAttachmentTask.Attachment attachment = new SaveAttachmentTask.Attachment(uri, MediaUtil.IMAGE_JPEG, System.currentTimeMillis(), null); + saveTask.executeOnExecutor(SignalExecutors.BOUNDED, attachment); + }); + }) + .execute(); + }); + } + @Override public void onFlipHorizontal() { imageEditorView.getModel().flipHorizontal(); diff --git a/src/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java b/src/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java index 85ada5c0a1..efc062b14d 100644 --- a/src/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java +++ b/src/org/thoughtcrime/securesms/scribbles/ImageEditorHud.java @@ -36,6 +36,7 @@ public final class ImageEditorHud extends LinearLayout { private View textButton; private View stickerButton; private View undoButton; + private View saveButton; private View deleteButton; private View confirmButton; private VerticalSlideColorPicker colorPicker; @@ -81,6 +82,7 @@ public final class ImageEditorHud extends LinearLayout { textButton = findViewById(R.id.scribble_text_button); stickerButton = findViewById(R.id.scribble_sticker_button); undoButton = findViewById(R.id.scribble_undo_button); + saveButton = findViewById(R.id.scribble_save_button); deleteButton = findViewById(R.id.scribble_delete_button); confirmButton = findViewById(R.id.scribble_confirm_button); colorPicker = findViewById(R.id.scribble_color_picker); @@ -100,7 +102,7 @@ public final class ImageEditorHud extends LinearLayout { } private void initializeVisibilityMap() { - setVisibleViewsWhenInMode(Mode.NONE, drawButton, highlightButton, textButton, stickerButton, cropButton, undoButton); + setVisibleViewsWhenInMode(Mode.NONE, drawButton, highlightButton, textButton, stickerButton, cropButton, undoButton, saveButton); setVisibleViewsWhenInMode(Mode.DRAW, confirmButton, undoButton, colorPicker, colorPalette); @@ -145,6 +147,7 @@ public final class ImageEditorHud extends LinearLayout { highlightButton.setOnClickListener(v -> setMode(Mode.HIGHLIGHT)); textButton.setOnClickListener(v -> setMode(Mode.TEXT)); stickerButton.setOnClickListener(v -> setMode(Mode.MOVE_DELETE)); + saveButton.setOnClickListener(v -> eventListener.onSave()); } public void setColorPalette(@NonNull Set colors) { @@ -241,6 +244,7 @@ public final class ImageEditorHud extends LinearLayout { void onColorChange(int color); void onUndo(); void onDelete(); + void onSave(); void onFlipHorizontal(); void onRotate90AntiClockwise(); void onCropAspectLock(boolean locked); @@ -266,6 +270,10 @@ public final class ImageEditorHud extends LinearLayout { public void onDelete() { } + @Override + public void onSave() { + } + @Override public void onFlipHorizontal() { }