fedilab-Android-App/app/src/main/java/app/fedilab/android/activities/PhotoEditorActivity.java

559 lines
21 KiB
Java
Raw Normal View History

2019-05-30 15:19:02 +02:00
package app.fedilab.android.activities;
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
2019-11-15 16:32:25 +01:00
import android.Manifest;
2019-05-30 15:19:02 +02:00
import android.annotation.SuppressLint;
2019-11-15 16:32:25 +01:00
import android.content.Intent;
2019-05-30 15:19:02 +02:00
import android.content.SharedPreferences;
2020-06-19 17:27:59 +02:00
import android.content.pm.PackageManager;
2019-11-15 16:32:25 +01:00
import android.graphics.Bitmap;
import android.graphics.Typeface;
2019-05-30 15:19:02 +02:00
import android.net.Uri;
import android.os.Bundle;
2019-11-15 16:32:25 +01:00
import android.provider.MediaStore;
import android.view.View;
import android.view.animation.AnticipateOvershootInterpolator;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
2019-09-06 17:55:14 +02:00
2019-06-11 19:38:26 +02:00
import androidx.annotation.NonNull;
2019-11-15 16:32:25 +01:00
import androidx.appcompat.app.AlertDialog;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
2020-06-19 17:27:59 +02:00
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
2019-08-04 11:22:49 +02:00
import androidx.exifinterface.media.ExifInterface;
2019-06-11 19:38:26 +02:00
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
2019-11-15 16:32:25 +01:00
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.transition.ChangeBounds;
import androidx.transition.TransitionManager;
2019-08-04 11:22:49 +02:00
2019-11-15 16:32:25 +01:00
import com.theartofdev.edmodo.cropper.CropImage;
2019-05-30 15:19:02 +02:00
2019-11-15 16:32:25 +01:00
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
2019-05-30 15:19:02 +02:00
import app.fedilab.android.R;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.imageeditor.EmojiBSFragment;
import app.fedilab.android.imageeditor.PropertiesBSFragment;
import app.fedilab.android.imageeditor.StickerBSFragment;
import app.fedilab.android.imageeditor.TextEditorDialogFragment;
import app.fedilab.android.imageeditor.filters.FilterListener;
import app.fedilab.android.imageeditor.filters.FilterViewAdapter;
import app.fedilab.android.imageeditor.tools.EditingToolsAdapter;
import app.fedilab.android.imageeditor.tools.ToolType;
2020-04-25 11:18:42 +02:00
import es.dmoral.toasty.Toasty;
2019-11-15 16:32:25 +01:00
import ja.burhanrashid52.photoeditor.OnPhotoEditorListener;
2019-05-30 15:19:02 +02:00
import ja.burhanrashid52.photoeditor.PhotoEditor;
import ja.burhanrashid52.photoeditor.PhotoEditorView;
import ja.burhanrashid52.photoeditor.PhotoFilter;
import ja.burhanrashid52.photoeditor.SaveSettings;
import ja.burhanrashid52.photoeditor.ViewType;
2019-09-06 17:55:14 +02:00
public class PhotoEditorActivity extends BaseActivity implements OnPhotoEditorListener,
2019-05-30 15:19:02 +02:00
View.OnClickListener,
PropertiesBSFragment.Properties,
EmojiBSFragment.EmojiListener,
StickerBSFragment.StickerListener, EditingToolsAdapter.OnItemSelected, FilterListener {
private static final int CAMERA_REQUEST = 52;
private static final int PICK_REQUEST = 53;
2020-06-19 17:27:59 +02:00
private final int STORE_REQUEST = 54;
2019-05-30 15:19:02 +02:00
private PhotoEditor mPhotoEditor;
private PhotoEditorView mPhotoEditorView;
private PropertiesBSFragment mPropertiesBSFragment;
private EmojiBSFragment mEmojiBSFragment;
private StickerBSFragment mStickerBSFragment;
private TextView mTxtCurrentTool;
private RecyclerView mRvTools, mRvFilters;
private EditingToolsAdapter mEditingToolsAdapter = new EditingToolsAdapter(this);
private FilterViewAdapter mFilterViewAdapter = new FilterViewAdapter(this);
private ConstraintLayout mRootView;
private ConstraintSet mConstraintSet = new ConstraintSet();
private boolean mIsFilterVisible;
2019-05-30 16:37:49 +02:00
private Uri uri;
private boolean exit;
2019-05-30 15:19:02 +02:00
2019-11-15 16:32:25 +01:00
private static int exifToDegrees(int exifOrientation) {
if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
return 90;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
return 180;
} else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
return 270;
}
return 0;
}
2019-05-30 15:19:02 +02:00
@SuppressLint("MissingPermission")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK);
2019-09-06 17:55:14 +02:00
switch (theme) {
2019-05-30 15:19:02 +02:00
case Helper.THEME_LIGHT:
2019-11-09 15:47:38 +01:00
setTheme(R.style.AppTheme_Fedilab);
2019-05-30 15:19:02 +02:00
break;
case Helper.THEME_BLACK:
setTheme(R.style.AppThemeBlack);
break;
default:
setTheme(R.style.AppThemeDark);
}
Bundle b = getIntent().getExtras();
2019-09-06 17:55:14 +02:00
if (getSupportActionBar() != null)
2019-05-30 15:19:02 +02:00
getSupportActionBar().hide();
String path = null;
2019-09-06 17:55:14 +02:00
if (b != null)
2019-05-30 15:19:02 +02:00
path = b.getString("imageUri", null);
2019-09-06 17:55:14 +02:00
if (path == null) {
2019-05-30 15:19:02 +02:00
finish();
}
2019-05-30 16:37:49 +02:00
uri = Uri.parse(path);
2019-05-30 15:19:02 +02:00
2019-05-30 16:37:49 +02:00
exit = false;
2019-05-30 15:19:02 +02:00
setContentView(R.layout.activity_photoeditor);
initViews();
mPropertiesBSFragment = new PropertiesBSFragment();
mEmojiBSFragment = new EmojiBSFragment();
mStickerBSFragment = new StickerBSFragment();
mStickerBSFragment.setStickerListener(this);
mEmojiBSFragment.setEmojiListener(this);
mPropertiesBSFragment.setPropertiesChangeListener(this);
LinearLayoutManager llmTools = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
mRvTools.setLayoutManager(llmTools);
mRvTools.setAdapter(mEditingToolsAdapter);
LinearLayoutManager llmFilters = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
mRvFilters.setLayoutManager(llmFilters);
mRvFilters.setAdapter(mFilterViewAdapter);
Typeface mEmojiTypeFace = Typeface.createFromAsset(getAssets(), "emojione-android.ttf");
mPhotoEditor = new PhotoEditor.Builder(this, mPhotoEditorView)
.setPinchTextScalable(true) // set flag to make text scalable when pinch
.setDefaultEmojiTypeface(mEmojiTypeFace)
.build(); // build photo editor sdk
2019-05-30 19:09:36 +02:00
2019-05-30 15:19:02 +02:00
mPhotoEditor.setOnPhotoEditorListener(this);
//Set Image Dynamically
2020-04-25 11:18:42 +02:00
try {
mPhotoEditorView.getSource().setImageURI(uri);
2020-04-25 19:20:42 +02:00
} catch (Exception e) {
2020-04-25 11:18:42 +02:00
Toasty.error(PhotoEditorActivity.this, getString(R.string.error)).show();
}
2019-05-30 16:37:49 +02:00
2019-09-06 17:55:14 +02:00
if (uri != null) {
2019-08-04 11:22:49 +02:00
try (InputStream inputStream = getContentResolver().openInputStream(uri)) {
assert inputStream != null;
ExifInterface exif = new ExifInterface(inputStream);
int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int rotationInDegrees = exifToDegrees(rotation);
mPhotoEditorView.getSource().setRotation(rotationInDegrees);
} catch (Exception e) {
e.printStackTrace();
}
}
2019-05-30 16:37:49 +02:00
Button send = findViewById(R.id.send);
2019-11-19 19:10:52 +01:00
send.setOnClickListener(v -> {
exit = true;
saveImage();
2019-05-30 16:37:49 +02:00
});
2019-05-30 15:19:02 +02:00
}
private void initViews() {
ImageView imgUndo;
ImageView imgRedo;
ImageView imgCamera;
ImageView imgGallery;
ImageView imgSave;
ImageView imgClose;
mPhotoEditorView = findViewById(R.id.photoEditorView);
mTxtCurrentTool = findViewById(R.id.txtCurrentTool);
mRvTools = findViewById(R.id.rvConstraintTools);
mRvFilters = findViewById(R.id.rvFilterView);
mRootView = findViewById(R.id.rootView);
imgUndo = findViewById(R.id.imgUndo);
imgUndo.setOnClickListener(this);
imgRedo = findViewById(R.id.imgRedo);
imgRedo.setOnClickListener(this);
imgCamera = findViewById(R.id.imgCamera);
imgCamera.setOnClickListener(this);
imgGallery = findViewById(R.id.imgGallery);
imgGallery.setOnClickListener(this);
imgSave = findViewById(R.id.imgSave);
imgSave.setOnClickListener(this);
imgClose = findViewById(R.id.imgClose);
imgClose.setOnClickListener(this);
}
@Override
public void onEditTextChangeListener(final View rootView, String text, int colorCode) {
TextEditorDialogFragment textEditorDialogFragment =
TextEditorDialogFragment.show(this, text, colorCode);
2020-04-08 13:01:28 +02:00
textEditorDialogFragment.setOnTextEditorListener((inputText, colorCode1) -> {
mPhotoEditor.editText(rootView, inputText, colorCode1);
mTxtCurrentTool.setText(R.string.label_text);
2019-05-30 15:19:02 +02:00
});
}
@Override
public void onAddViewListener(ViewType viewType, int numberOfAddedViews) {
}
@Override
public void onRemoveViewListener(ViewType viewType, int numberOfAddedViews) {
}
@Override
public void onStartViewChangeListener(ViewType viewType) {
}
@Override
public void onStopViewChangeListener(ViewType viewType) {
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.imgUndo:
mPhotoEditor.undo();
break;
case R.id.imgRedo:
mPhotoEditor.redo();
break;
case R.id.imgSave:
saveImage();
break;
case R.id.imgClose:
onBackPressed();
break;
case R.id.imgCamera:
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
break;
case R.id.imgGallery:
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
2019-05-30 15:55:09 +02:00
startActivityForResult(Intent.createChooser(intent, getString(R.string.toot_select_image)), PICK_REQUEST);
2019-05-30 15:19:02 +02:00
break;
}
}
2019-11-19 19:10:52 +01:00
2019-05-30 15:19:02 +02:00
private void saveImage() {
if (requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
2019-05-30 15:55:09 +02:00
showLoading(getString(R.string.saving));
2019-05-30 16:37:49 +02:00
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
2019-06-23 18:18:20 +02:00
String myDir = sharedpreferences.getString(Helper.SET_FOLDER_RECORD, getCacheDir().getAbsolutePath());
2019-09-06 17:55:14 +02:00
String filename = System.currentTimeMillis() + "_" + Helper.getFileName(PhotoEditorActivity.this, uri);
2019-05-30 16:37:49 +02:00
2019-09-06 17:55:14 +02:00
File file = new File(myDir + "/" + filename);
2019-05-30 15:19:02 +02:00
try {
2020-04-08 13:01:28 +02:00
//noinspection ResultOfMethodCallIgnored
2019-05-30 15:19:02 +02:00
file.createNewFile();
SaveSettings saveSettings = new SaveSettings.Builder()
.setClearViewsEnabled(true)
.setTransparencyEnabled(true)
.build();
2020-06-19 17:27:59 +02:00
if (ContextCompat.checkSelfPermission(PhotoEditorActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(PhotoEditorActivity.this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
STORE_REQUEST);
return;
}
2019-05-30 15:19:02 +02:00
mPhotoEditor.saveAsFile(file.getAbsolutePath(), saveSettings, new PhotoEditor.OnSaveListener() {
@Override
public void onSuccess(@NonNull String imagePath) {
hideLoading();
2019-05-30 15:55:09 +02:00
showSnackbar(getString(R.string.image_saved));
2019-05-30 15:19:02 +02:00
mPhotoEditorView.getSource().setImageURI(Uri.fromFile(new File(imagePath)));
2019-09-06 17:55:14 +02:00
if (exit) {
2019-05-30 16:37:49 +02:00
Intent intentImage = new Intent(Helper.INTENT_SEND_MODIFIED_IMAGE);
intentImage.putExtra("imgpath", imagePath);
2020-04-08 12:42:15 +02:00
LocalBroadcastManager.getInstance(PhotoEditorActivity.this).sendBroadcast(intentImage);
2019-05-30 16:37:49 +02:00
finish();
}
2019-05-30 15:19:02 +02:00
}
@Override
public void onFailure(@NonNull Exception exception) {
hideLoading();
2019-05-30 15:55:09 +02:00
showSnackbar(getString(R.string.save_image_failed));
2019-05-30 15:19:02 +02:00
}
});
} catch (IOException e) {
e.printStackTrace();
hideLoading();
2019-11-19 19:10:52 +01:00
if (e.getMessage() != null) {
showSnackbar(e.getMessage());
}
2019-05-30 15:19:02 +02:00
}
}
}
2020-06-19 17:27:59 +02:00
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == STORE_REQUEST) {// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// We have the permission.
saveImage();
}
}
}
2019-05-30 15:19:02 +02:00
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
2019-10-28 18:09:40 +01:00
super.onActivityResult(requestCode, resultCode, data);
2019-05-30 15:19:02 +02:00
if (resultCode == RESULT_OK) {
2019-11-19 19:10:52 +01:00
ExifInterface exif;
int rotation;
2019-08-04 11:22:49 +02:00
int rotationInDegrees = 0;
2019-09-06 17:55:14 +02:00
if (data != null && data.getData() != null) {
2019-08-04 11:22:49 +02:00
try (InputStream inputStream = getContentResolver().openInputStream(data.getData())) {
assert inputStream != null;
exif = new androidx.exifinterface.media.ExifInterface(inputStream);
rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
rotationInDegrees = exifToDegrees(rotation);
} catch (Exception e) {
e.printStackTrace();
}
}
2019-11-19 19:10:52 +01:00
2019-05-30 15:19:02 +02:00
switch (requestCode) {
case CAMERA_REQUEST:
2020-03-08 10:29:06 +01:00
if (data != null && data.getExtras() != null) {
2019-05-30 15:19:02 +02:00
mPhotoEditor.clearAllViews();
2019-11-19 19:10:52 +01:00
Bitmap photo = (Bitmap) data.getExtras().get("data");
mPhotoEditorView.getSource().setImageBitmap(photo);
2019-08-04 11:22:49 +02:00
mPhotoEditorView.getSource().setRotation(rotationInDegrees);
2019-11-19 19:10:52 +01:00
}
break;
case PICK_REQUEST:
2020-03-08 10:29:06 +01:00
if (data != null && data.getData() != null) {
2019-11-19 19:10:52 +01:00
try {
mPhotoEditor.clearAllViews();
Uri uri = data.getData();
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
mPhotoEditorView.getSource().setImageBitmap(bitmap);
mPhotoEditorView.getSource().setRotation(rotationInDegrees);
} catch (IOException e) {
e.printStackTrace();
}
2019-05-30 15:19:02 +02:00
}
break;
2019-08-24 16:39:53 +02:00
case CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE:
2019-11-19 19:10:52 +01:00
2019-08-24 16:39:53 +02:00
CropImage.ActivityResult result = CropImage.getActivityResult(data);
2019-11-19 19:10:52 +01:00
if (result != null) {
Uri resultUri = result.getUri();
if (resultUri != null) {
mPhotoEditorView.getSource().setImageURI(resultUri);
mPhotoEditorView.getSource().setRotation(rotationInDegrees);
if (uri != null && uri.getPath() != null) {
File fdelete = new File(uri.getPath());
if (fdelete.exists()) {
//noinspection ResultOfMethodCallIgnored
fdelete.delete();
}
}
uri = resultUri;
2019-05-30 19:09:36 +02:00
}
}
break;
2019-05-30 15:19:02 +02:00
}
}
2019-11-19 19:10:52 +01:00
2019-05-30 15:19:02 +02:00
}
@Override
public void onColorChanged(int colorCode) {
mPhotoEditor.setBrushColor(colorCode);
mTxtCurrentTool.setText(R.string.label_brush);
}
@Override
public void onOpacityChanged(int opacity) {
mPhotoEditor.setOpacity(opacity);
mTxtCurrentTool.setText(R.string.label_brush);
}
@Override
public void onBrushSizeChanged(int brushSize) {
mPhotoEditor.setBrushSize(brushSize);
mTxtCurrentTool.setText(R.string.label_brush);
}
@Override
public void onEmojiClick(String emojiUnicode) {
mPhotoEditor.addEmoji(emojiUnicode);
mTxtCurrentTool.setText(R.string.label_emoji);
}
@Override
public void onStickerClick(Bitmap bitmap) {
mPhotoEditor.addImage(bitmap);
mTxtCurrentTool.setText(R.string.label_sticker);
}
@Override
public void isPermissionGranted(boolean isGranted, String permission) {
if (isGranted) {
saveImage();
}
}
private void showSaveDialog() {
int style;
SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE);
int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK);
if (theme == Helper.THEME_DARK) {
style = R.style.DialogDark;
2019-09-06 17:55:14 +02:00
} else if (theme == Helper.THEME_BLACK) {
style = R.style.DialogBlack;
2019-09-06 17:55:14 +02:00
} else {
style = R.style.Dialog;
}
AlertDialog.Builder builder = new AlertDialog.Builder(this, style);
2019-09-06 17:55:14 +02:00
builder.setMessage(getString(R.string.confirm_exit_editing));
2020-04-08 13:01:28 +02:00
builder.setPositiveButton(R.string.save, (dialog, which) -> saveImage());
builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
2019-05-30 15:19:02 +02:00
2020-04-08 13:01:28 +02:00
builder.setNegativeButton(R.string.discard, (dialog, which) -> finish());
2019-05-30 15:19:02 +02:00
builder.create().show();
}
@Override
public void onFilterSelected(PhotoFilter photoFilter) {
mPhotoEditor.setFilterEffect(photoFilter);
}
@Override
public void onToolSelected(ToolType toolType) {
switch (toolType) {
case BRUSH:
mPhotoEditor.setBrushDrawingMode(true);
mTxtCurrentTool.setText(R.string.label_brush);
mPropertiesBSFragment.show(getSupportFragmentManager(), mPropertiesBSFragment.getTag());
break;
case TEXT:
TextEditorDialogFragment textEditorDialogFragment = TextEditorDialogFragment.show(this);
2020-04-08 13:01:28 +02:00
textEditorDialogFragment.setOnTextEditorListener((inputText, colorCode) -> {
mPhotoEditor.addText(inputText, colorCode);
mTxtCurrentTool.setText(R.string.label_text);
2019-05-30 15:19:02 +02:00
});
break;
case ERASER:
mPhotoEditor.brushEraser();
mTxtCurrentTool.setText(R.string.label_eraser);
break;
case FILTER:
mTxtCurrentTool.setText(R.string.label_filter);
showFilter(true);
break;
case EMOJI:
mEmojiBSFragment.show(getSupportFragmentManager(), mEmojiBSFragment.getTag());
break;
case STICKER:
mStickerBSFragment.show(getSupportFragmentManager(), mStickerBSFragment.getTag());
break;
2019-05-30 19:09:36 +02:00
case CROP:
2019-08-24 16:39:53 +02:00
CropImage.activity(uri)
.start(this);
2019-05-30 19:09:36 +02:00
break;
2019-05-30 15:19:02 +02:00
}
}
void showFilter(boolean isVisible) {
mIsFilterVisible = isVisible;
mConstraintSet.clone(mRootView);
if (isVisible) {
mConstraintSet.clear(mRvFilters.getId(), ConstraintSet.START);
mConstraintSet.connect(mRvFilters.getId(), ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.START);
mConstraintSet.connect(mRvFilters.getId(), ConstraintSet.END,
ConstraintSet.PARENT_ID, ConstraintSet.END);
} else {
mConstraintSet.connect(mRvFilters.getId(), ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.END);
mConstraintSet.clear(mRvFilters.getId(), ConstraintSet.END);
}
ChangeBounds changeBounds = new ChangeBounds();
changeBounds.setDuration(350);
changeBounds.setInterpolator(new AnticipateOvershootInterpolator(1.0f));
TransitionManager.beginDelayedTransition(mRootView, changeBounds);
mConstraintSet.applyTo(mRootView);
}
@Override
public void onBackPressed() {
if (mIsFilterVisible) {
showFilter(false);
mTxtCurrentTool.setText(R.string.app_name);
} else if (!mPhotoEditor.isCacheEmpty()) {
showSaveDialog();
} else {
super.onBackPressed();
}
}
}