Adding media editor

This commit is contained in:
Thomas 2022-06-05 19:30:31 +02:00
parent 422119d44d
commit 12e352c774
7 changed files with 168 additions and 285 deletions

View file

@ -86,6 +86,9 @@
android:name=".activities.ScheduledActivity" android:name=".activities.ScheduledActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/scheduled" /> android:label="@string/scheduled" />
<activity
android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
android:theme="@style/Base.Theme.AppCompat" />
<service <service
android:name=".services.PostMessageService" android:name=".services.PostMessageService"

View file

@ -19,8 +19,10 @@ import static app.fedilab.android.ui.drawer.ComposeAdapter.prepareDraft;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.ClipData; import android.content.ClipData;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
@ -42,6 +44,7 @@ import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.work.Data; import androidx.work.Data;
import androidx.work.OneTimeWorkRequest; import androidx.work.OneTimeWorkRequest;
@ -58,6 +61,7 @@ import java.util.concurrent.TimeUnit;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Context; import app.fedilab.android.client.entities.api.Context;
import app.fedilab.android.client.entities.api.Mention; import app.fedilab.android.client.entities.api.Mention;
import app.fedilab.android.client.entities.api.ScheduledStatus; import app.fedilab.android.client.entities.api.ScheduledStatus;
@ -103,6 +107,27 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
private app.fedilab.android.client.entities.api.Account accountMention; private app.fedilab.android.client.entities.api.Account accountMention;
private String statusReplyId; private String statusReplyId;
private final BroadcastReceiver imageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(android.content.Context context, Intent intent) {
String imgpath = intent.getStringExtra("imgpath");
if (imgpath != null) {
int position = 0;
for (Status status : statusList) {
if (status.media_attachments != null && status.media_attachments.size() > 0) {
for (Attachment attachment : status.media_attachments) {
if (attachment.local_path.equalsIgnoreCase(imgpath)) {
composeAdapter.notifyItemChanged(position);
break;
}
}
}
position++;
}
}
}
};
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -272,6 +297,17 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
MastodonHelper.loadPPMastodon(binding.profilePicture, account.mastodon_account); MastodonHelper.loadPPMastodon(binding.profilePicture, account.mastodon_account);
LocalBroadcastManager.getInstance(this)
.registerReceiver(imageReceiver,
new IntentFilter(Helper.INTENT_SEND_MODIFIED_IMAGE));
}
@Override
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(imageReceiver);
} }
@Override @Override

View file

@ -1,34 +1,27 @@
package app.fedilab.android.imageeditor; package app.fedilab.android.imageeditor;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static app.fedilab.android.imageeditor.FileSaveHelper.isSdkHigherThan28;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.util.Log;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.animation.AnticipateOvershootInterpolator; import android.view.animation.AnticipateOvershootInterpolator;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet; import androidx.constraintlayout.widget.ConstraintSet;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.exifinterface.media.ExifInterface; import androidx.exifinterface.media.ExifInterface;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.transition.ChangeBounds; import androidx.transition.ChangeBounds;
import androidx.transition.TransitionManager; import androidx.transition.TransitionManager;
@ -40,6 +33,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityEditImageBinding;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.imageeditor.base.BaseActivity; import app.fedilab.android.imageeditor.base.BaseActivity;
import app.fedilab.android.imageeditor.filters.FilterListener; import app.fedilab.android.imageeditor.filters.FilterListener;
import app.fedilab.android.imageeditor.filters.FilterViewAdapter; import app.fedilab.android.imageeditor.filters.FilterViewAdapter;
@ -48,7 +43,6 @@ import app.fedilab.android.imageeditor.tools.ToolType;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
import ja.burhanrashid52.photoeditor.OnPhotoEditorListener; import ja.burhanrashid52.photoeditor.OnPhotoEditorListener;
import ja.burhanrashid52.photoeditor.PhotoEditor; import ja.burhanrashid52.photoeditor.PhotoEditor;
import ja.burhanrashid52.photoeditor.PhotoEditorView;
import ja.burhanrashid52.photoeditor.PhotoFilter; import ja.burhanrashid52.photoeditor.PhotoFilter;
import ja.burhanrashid52.photoeditor.SaveSettings; import ja.burhanrashid52.photoeditor.SaveSettings;
import ja.burhanrashid52.photoeditor.TextStyleBuilder; import ja.burhanrashid52.photoeditor.TextStyleBuilder;
@ -60,35 +54,23 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
View.OnClickListener, View.OnClickListener,
PropertiesBSFragment.Properties, PropertiesBSFragment.Properties,
ShapeBSFragment.Properties, ShapeBSFragment.Properties,
EmojiBSFragment.EmojiListener, EmojiBSFragment.EmojiListener, EditingToolsAdapter.OnItemSelected, FilterListener {
StickerBSFragment.StickerListener, EditingToolsAdapter.OnItemSelected, FilterListener {
public static final String FILE_PROVIDER_AUTHORITY = "com.burhanrashid52.photoeditor.fileprovider";
public static final String ACTION_NEXTGEN_EDIT = "action_nextgen_edit";
public static final String PINCH_TEXT_SCALABLE_INTENT_KEY = "PINCH_TEXT_SCALABLE";
private static final int CAMERA_REQUEST = 52; private static final int CAMERA_REQUEST = 52;
private static final int PICK_REQUEST = 53; private static final int PICK_REQUEST = 53;
private final int STORE_REQUEST = 54;
private final EditingToolsAdapter mEditingToolsAdapter = new EditingToolsAdapter(this); private final EditingToolsAdapter mEditingToolsAdapter = new EditingToolsAdapter(this);
private final FilterViewAdapter mFilterViewAdapter = new FilterViewAdapter(this); private final FilterViewAdapter mFilterViewAdapter = new FilterViewAdapter(this);
private final ConstraintSet mConstraintSet = new ConstraintSet(); private final ConstraintSet mConstraintSet = new ConstraintSet();
PhotoEditor mPhotoEditor; PhotoEditor mPhotoEditor;
@Nullable
@VisibleForTesting
Uri mSaveImageUri;
private PhotoEditorView mPhotoEditorView;
private PropertiesBSFragment mPropertiesBSFragment; private PropertiesBSFragment mPropertiesBSFragment;
private ShapeBSFragment mShapeBSFragment; private ShapeBSFragment mShapeBSFragment;
private ShapeBuilder mShapeBuilder; private ShapeBuilder mShapeBuilder;
private EmojiBSFragment mEmojiBSFragment; private EmojiBSFragment mEmojiBSFragment;
private StickerBSFragment mStickerBSFragment;
private TextView mTxtCurrentTool;
private Typeface mWonderFont;
private RecyclerView mRvTools, mRvFilters;
private ConstraintLayout mRootView;
private boolean mIsFilterVisible; private boolean mIsFilterVisible;
private Uri uri; private Uri uri;
private boolean exit; private boolean exit;
private FileSaveHelper mSaveFileHelper;
private static int exifToDegrees(int exifOrientation) { private static int exifToDegrees(int exifOrientation) {
if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) { if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
@ -101,59 +83,47 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
return 0; return 0;
} }
String path;
private ActivityEditImageBinding binding;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
makeFullScreen();
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit_image); binding = ActivityEditImageBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
if (getSupportActionBar() != null) if (getSupportActionBar() != null)
getSupportActionBar().hide(); getSupportActionBar().hide();
String path = null;
if (b != null) if (b != null)
path = b.getString("imageUri", null); path = b.getString("imageUri", null);
if (path == null) { if (path == null) {
finish(); finish();
} }
uri = Uri.parse(path); uri = Uri.parse("file://" + path);
exit = false; exit = false;
initViews(); initViews();
handleIntentImage(mPhotoEditorView.getSource());
mWonderFont = Typeface.createFromAsset(getAssets(), "beyond_wonderland.ttf");
mPropertiesBSFragment = new PropertiesBSFragment(); mPropertiesBSFragment = new PropertiesBSFragment();
mEmojiBSFragment = new EmojiBSFragment(); mEmojiBSFragment = new EmojiBSFragment();
mStickerBSFragment = new StickerBSFragment();
mShapeBSFragment = new ShapeBSFragment(); mShapeBSFragment = new ShapeBSFragment();
mStickerBSFragment.setStickerListener(this);
mEmojiBSFragment.setEmojiListener(this); mEmojiBSFragment.setEmojiListener(this);
mPropertiesBSFragment.setPropertiesChangeListener(this); mPropertiesBSFragment.setPropertiesChangeListener(this);
mShapeBSFragment.setPropertiesChangeListener(this); mShapeBSFragment.setPropertiesChangeListener(this);
LinearLayoutManager llmTools = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); LinearLayoutManager llmTools = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
mRvTools.setLayoutManager(llmTools); binding.rvConstraintTools.setLayoutManager(llmTools);
mRvTools.setAdapter(mEditingToolsAdapter); binding.rvConstraintTools.setAdapter(mEditingToolsAdapter);
LinearLayoutManager llmFilters = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); LinearLayoutManager llmFilters = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
mRvFilters.setLayoutManager(llmFilters); binding.rvFilterView.setLayoutManager(llmFilters);
mRvFilters.setAdapter(mFilterViewAdapter); binding.rvFilterView.setAdapter(mFilterViewAdapter);
// NOTE(lucianocheng): Used to set integration testing parameters to PhotoEditor
boolean pinchTextScalable = getIntent().getBooleanExtra(PINCH_TEXT_SCALABLE_INTENT_KEY, true);
//Typeface mTextRobotoTf = ResourcesCompat.getFont(this, R.font.roboto_medium); //Typeface mTextRobotoTf = ResourcesCompat.getFont(this, R.font.roboto_medium);
//Typeface mEmojiTypeFace = Typeface.createFromAsset(getAssets(), "emojione-android.ttf"); //Typeface mEmojiTypeFace = Typeface.createFromAsset(getAssets(), "emojione-android.ttf");
Typeface mEmojiTypeFace = Typeface.createFromAsset(getAssets(), "emojione-android.ttf"); Typeface mEmojiTypeFace = Typeface.createFromAsset(getAssets(), "emojione-android.ttf");
mPhotoEditor = new PhotoEditor.Builder(this, mPhotoEditorView) mPhotoEditor = new PhotoEditor.Builder(this, binding.photoEditorView)
.setPinchTextScalable(pinchTextScalable) // set flag to make text scalable when pinch
//.setDefaultTextTypeface(mTextRobotoTf)
.setPinchTextScalable(true) .setPinchTextScalable(true)
.setDefaultEmojiTypeface(mEmojiTypeFace) .setDefaultEmojiTypeface(mEmojiTypeFace)
.build(); // build photo editor sdk .build(); // build photo editor sdk
@ -163,93 +133,46 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
//Set Image Dynamically //Set Image Dynamically
try { try {
mPhotoEditorView.getSource().setImageURI(uri); binding.photoEditorView.getSource().setImageURI(uri);
} catch (Exception e) { } catch (Exception e) {
Toasty.error(EditImageActivity.this, getString(R.string.toast_error)).show(); Toasty.error(EditImageActivity.this, getString(R.string.toast_error)).show();
} }
if (uri != null) { if (uri != null) {
try (InputStream inputStream = getContentResolver().openInputStream(uri)) { try (InputStream inputStream = getContentResolver().openInputStream(uri)) {
assert inputStream != null;
ExifInterface exif = new ExifInterface(inputStream); ExifInterface exif = new ExifInterface(inputStream);
int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int rotationInDegrees = exifToDegrees(rotation); int rotationInDegrees = exifToDegrees(rotation);
mPhotoEditorView.getSource().setRotation(rotationInDegrees); binding.photoEditorView.getSource().setRotation(rotationInDegrees);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
Button send = findViewById(R.id.send); mPhotoEditor.setFilterEffect(PhotoFilter.NONE);
binding.send.setOnClickListener(v -> {
send.setOnClickListener(v -> {
exit = true; exit = true;
saveImage(); saveImage();
}); });
} }
private void handleIntentImage(ImageView source) { @Override
Intent intent = getIntent(); public void onRequestPermissionsResult(int requestCode,
if (intent != null) { @NonNull String[] permissions, @NonNull int[] grantResults) {
// NOTE(lucianocheng): Using "yoda conditions" here to guard against super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// a null Action in the Intent. if (requestCode == STORE_REQUEST) {// If request is cancelled, the result arrays are empty.
if (Intent.ACTION_EDIT.equals(intent.getAction()) || if (grantResults.length > 0
ACTION_NEXTGEN_EDIT.equals(intent.getAction())) { && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
try { saveImage();
Uri uri = intent.getData();
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
source.setImageBitmap(bitmap);
} catch (IOException e) {
e.printStackTrace();
}
} else {
String intentType = intent.getType();
if (intentType != null && intentType.startsWith("image/")) {
Uri imageUri = intent.getData();
if (imageUri != null) {
source.setImageURI(imageUri);
}
}
} }
} }
} }
private void initViews() { private void initViews() {
ImageView imgUndo; binding.imgUndo.setOnClickListener(this);
ImageView imgRedo; binding.imgRedo.setOnClickListener(this);
ImageView imgCamera; binding.imgClose.setOnClickListener(this);
ImageView imgGallery;
ImageView imgSave;
ImageView imgClose;
ImageView imgCrop;
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);
imgCrop = findViewById(R.id.imgCrop);
imgCrop.setOnClickListener(this);
} }
@Override @Override
@ -261,7 +184,7 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
styleBuilder.withTextColor(newColorCode); styleBuilder.withTextColor(newColorCode);
mPhotoEditor.editText(rootView, inputText, styleBuilder); mPhotoEditor.editText(rootView, inputText, styleBuilder);
mTxtCurrentTool.setText(R.string.label_text); binding.txtCurrentTool.setText(R.string.label_text);
}); });
} }
@ -285,100 +208,64 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
public void onTouchSourceImage(MotionEvent event) { public void onTouchSourceImage(MotionEvent event) {
} }
@SuppressLint("NonConstantResourceId")
@Override @Override
public void onClick(View view) { public void onClick(View view) {
switch (view.getId()) { int id = view.getId();
if (id == R.id.imgUndo) {
case R.id.imgUndo: mPhotoEditor.undo();
mPhotoEditor.undo(); } else if (id == R.id.imgRedo) {
break; mPhotoEditor.redo();
} else if (id == R.id.imgClose) {
case R.id.imgRedo: onBackPressed();
mPhotoEditor.redo();
break;
case R.id.imgSave:
saveImage();
break;
case R.id.imgClose:
onBackPressed();
break;
case R.id.imgCrop:
shareImage();
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);
startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_REQUEST);
break;
} }
} }
private void shareImage() {
if (mSaveImageUri == null) {
//showSnackbar(getString(R.string.msg_save_image_to_share));
return;
}
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_STREAM, buildFileProviderUri(mSaveImageUri));
startActivity(Intent.createChooser(intent, getString(R.string.msg_share_image)));
}
private Uri buildFileProviderUri(@NonNull Uri uri) {
return FileProvider.getUriForFile(this,
FILE_PROVIDER_AUTHORITY,
new File(uri.getPath()));
}
private void saveImage() { private void saveImage() {
final String fileName = System.currentTimeMillis() + ".png"; if (requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
final boolean hasStoragePermission = showLoading(getString(R.string.saving));
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PERMISSION_GRANTED; File file = new File(path);
if (hasStoragePermission || isSdkHigherThan28()) { try {
showLoading("Saving..."); //noinspection ResultOfMethodCallIgnored
mSaveFileHelper.createFile(fileName, (fileCreated, filePath, error, uri) -> { file.createNewFile();
if (fileCreated) {
SaveSettings saveSettings = new SaveSettings.Builder()
.setClearViewsEnabled(true)
.setTransparencyEnabled(true)
.build();
mPhotoEditor.saveAsFile(filePath, saveSettings, new PhotoEditor.OnSaveListener() { SaveSettings saveSettings = new SaveSettings.Builder()
@Override .setClearViewsEnabled(true)
public void onSuccess(@NonNull String imagePath) { .setTransparencyEnabled(true)
mSaveFileHelper.notifyThatFileIsNowPubliclyAvailable(getContentResolver()); .build();
hideLoading(); if (ContextCompat.checkSelfPermission(EditImageActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) !=
showSnackbar("Image Saved Successfully"); PackageManager.PERMISSION_GRANTED) {
mSaveImageUri = uri; ActivityCompat.requestPermissions(EditImageActivity.this,
mPhotoEditorView.getSource().setImageURI(mSaveImageUri); new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
} STORE_REQUEST);
return;
@Override
public void onFailure(@NonNull Exception exception) {
hideLoading();
showSnackbar("Failed to save Image");
}
});
} else {
hideLoading();
showSnackbar(error);
} }
}); mPhotoEditor.saveAsFile(file.getAbsolutePath(), saveSettings, new PhotoEditor.OnSaveListener() {
} else { @Override
requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE); public void onSuccess(@NonNull String imagePath) {
hideLoading();
showSnackbar(getString(R.string.image_saved));
binding.photoEditorView.getSource().setImageURI(Uri.fromFile(new File(imagePath)));
if (exit) {
Intent intentImage = new Intent(Helper.INTENT_SEND_MODIFIED_IMAGE);
intentImage.putExtra("imgpath", imagePath);
LocalBroadcastManager.getInstance(EditImageActivity.this).sendBroadcast(intentImage);
finish();
}
}
@Override
public void onFailure(@NonNull Exception exception) {
hideLoading();
showSnackbar(getString(R.string.save_image_failed));
}
});
} catch (IOException e) {
e.printStackTrace();
hideLoading();
if (e.getMessage() != null) {
showSnackbar(e.getMessage());
}
}
} }
} }
@ -406,8 +293,8 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
if (data != null && data.getExtras() != null) { if (data != null && data.getExtras() != null) {
mPhotoEditor.clearAllViews(); mPhotoEditor.clearAllViews();
Bitmap photo = (Bitmap) data.getExtras().get("data"); Bitmap photo = (Bitmap) data.getExtras().get("data");
mPhotoEditorView.getSource().setImageBitmap(photo); binding.photoEditorView.getSource().setImageBitmap(photo);
mPhotoEditorView.getSource().setRotation(rotationInDegrees); binding.photoEditorView.getSource().setRotation(rotationInDegrees);
} }
break; break;
case PICK_REQUEST: case PICK_REQUEST:
@ -416,8 +303,8 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
mPhotoEditor.clearAllViews(); mPhotoEditor.clearAllViews();
Uri uri = data.getData(); Uri uri = data.getData();
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri); Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
mPhotoEditorView.getSource().setImageBitmap(bitmap); binding.photoEditorView.getSource().setImageBitmap(bitmap);
mPhotoEditorView.getSource().setRotation(rotationInDegrees); binding.photoEditorView.getSource().setRotation(rotationInDegrees);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -429,8 +316,8 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
if (result != null) { if (result != null) {
Uri resultUri = result.getUri(); Uri resultUri = result.getUri();
if (resultUri != null) { if (resultUri != null) {
mPhotoEditorView.getSource().setImageURI(resultUri); binding.photoEditorView.getSource().setImageURI(resultUri);
mPhotoEditorView.getSource().setRotation(rotationInDegrees); binding.photoEditorView.getSource().setRotation(rotationInDegrees);
if (uri != null && uri.getPath() != null) { if (uri != null && uri.getPath() != null) {
File fdelete = new File(uri.getPath()); File fdelete = new File(uri.getPath());
if (fdelete.exists()) { if (fdelete.exists()) {
@ -449,19 +336,19 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
@Override @Override
public void onColorChanged(int colorCode) { public void onColorChanged(int colorCode) {
mPhotoEditor.setShape(mShapeBuilder.withShapeColor(colorCode)); mPhotoEditor.setShape(mShapeBuilder.withShapeColor(colorCode));
mTxtCurrentTool.setText(R.string.label_brush); binding.txtCurrentTool.setText(R.string.label_brush);
} }
@Override @Override
public void onOpacityChanged(int opacity) { public void onOpacityChanged(int opacity) {
mPhotoEditor.setShape(mShapeBuilder.withShapeOpacity(opacity)); mPhotoEditor.setShape(mShapeBuilder.withShapeOpacity(opacity));
mTxtCurrentTool.setText(R.string.label_brush); binding.txtCurrentTool.setText(R.string.label_brush);
} }
@Override @Override
public void onShapeSizeChanged(int shapeSize) { public void onShapeSizeChanged(int shapeSize) {
mPhotoEditor.setShape(mShapeBuilder.withShapeSize(shapeSize)); mPhotoEditor.setShape(mShapeBuilder.withShapeSize(shapeSize));
mTxtCurrentTool.setText(R.string.label_brush); binding.txtCurrentTool.setText(R.string.label_brush);
} }
@Override @Override
@ -472,22 +359,14 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
@Override @Override
public void onEmojiClick(String emojiUnicode) { public void onEmojiClick(String emojiUnicode) {
mPhotoEditor.addEmoji(emojiUnicode); mPhotoEditor.addEmoji(emojiUnicode);
mTxtCurrentTool.setText(R.string.label_emoji); binding.txtCurrentTool.setText(R.string.label_emoji);
} }
@Override
public void onStickerClick(Bitmap bitmap) {
mPhotoEditor.addImage(bitmap);
mTxtCurrentTool.setText(R.string.label_sticker);
}
private void showSaveDialog() { private void showSaveDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(getString(R.string.msg_save_image)); builder.setMessage(getString(R.string.msg_save_image));
builder.setPositiveButton("Save", (dialog, which) -> saveImage()); builder.setPositiveButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss()); builder.setNegativeButton(R.string.discard, (dialog, which) -> finish());
builder.setNeutralButton("Discard", (dialog, which) -> finish());
builder.create().show(); builder.create().show();
} }
@ -504,7 +383,7 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
mPhotoEditor.setBrushDrawingMode(true); mPhotoEditor.setBrushDrawingMode(true);
mShapeBuilder = new ShapeBuilder(); mShapeBuilder = new ShapeBuilder();
mPhotoEditor.setShape(mShapeBuilder); mPhotoEditor.setShape(mShapeBuilder);
mTxtCurrentTool.setText(R.string.label_shape); binding.txtCurrentTool.setText(R.string.label_shape);
showBottomSheetDialogFragment(mShapeBSFragment); showBottomSheetDialogFragment(mShapeBSFragment);
break; break;
case TEXT: case TEXT:
@ -514,28 +393,30 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
styleBuilder.withTextColor(colorCode); styleBuilder.withTextColor(colorCode);
mPhotoEditor.addText(inputText, styleBuilder); mPhotoEditor.addText(inputText, styleBuilder);
mTxtCurrentTool.setText(R.string.label_text); binding.txtCurrentTool.setText(R.string.label_text);
}); });
break; break;
case ERASER: case ERASER:
mPhotoEditor.brushEraser(); mPhotoEditor.brushEraser();
mTxtCurrentTool.setText(R.string.label_eraser_mode); binding.txtCurrentTool.setText(R.string.label_eraser_mode);
break; break;
case FILTER: case FILTER:
mTxtCurrentTool.setText(R.string.label_filter); binding.txtCurrentTool.setText(R.string.label_filter);
showFilter(true); showFilter(true);
break; break;
case EMOJI: case EMOJI:
showBottomSheetDialogFragment(mEmojiBSFragment); showBottomSheetDialogFragment(mEmojiBSFragment);
break; break;
case STICKER:
showBottomSheetDialogFragment(mStickerBSFragment);
break;
case BRUSH: case BRUSH:
mPhotoEditor.setBrushDrawingMode(true); mPhotoEditor.setBrushDrawingMode(true);
mTxtCurrentTool.setText(R.string.label_brush); binding.txtCurrentTool.setText(R.string.label_brush);
mPropertiesBSFragment.show(getSupportFragmentManager(), mPropertiesBSFragment.getTag()); mPropertiesBSFragment.show(getSupportFragmentManager(), mPropertiesBSFragment.getTag());
break; break;
case CROP:
Log.v(Helper.TAG, "crop! " + uri);
CropImage.activity(uri)
.start(this);
break;
} }
} }
@ -549,33 +430,33 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
void showFilter(boolean isVisible) { void showFilter(boolean isVisible) {
mIsFilterVisible = isVisible; mIsFilterVisible = isVisible;
mConstraintSet.clone(mRootView); mConstraintSet.clone(binding.rootView);
if (isVisible) { if (isVisible) {
mConstraintSet.clear(mRvFilters.getId(), ConstraintSet.START); mConstraintSet.clear(binding.rvFilterView.getId(), ConstraintSet.START);
mConstraintSet.connect(mRvFilters.getId(), ConstraintSet.START, mConstraintSet.connect(binding.rvFilterView.getId(), ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.START); ConstraintSet.PARENT_ID, ConstraintSet.START);
mConstraintSet.connect(mRvFilters.getId(), ConstraintSet.END, mConstraintSet.connect(binding.rvFilterView.getId(), ConstraintSet.END,
ConstraintSet.PARENT_ID, ConstraintSet.END); ConstraintSet.PARENT_ID, ConstraintSet.END);
} else { } else {
mConstraintSet.connect(mRvFilters.getId(), ConstraintSet.START, mConstraintSet.connect(binding.rvFilterView.getId(), ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.END); ConstraintSet.PARENT_ID, ConstraintSet.END);
mConstraintSet.clear(mRvFilters.getId(), ConstraintSet.END); mConstraintSet.clear(binding.rvFilterView.getId(), ConstraintSet.END);
} }
ChangeBounds changeBounds = new ChangeBounds(); ChangeBounds changeBounds = new ChangeBounds();
changeBounds.setDuration(350); changeBounds.setDuration(350);
changeBounds.setInterpolator(new AnticipateOvershootInterpolator(1.0f)); changeBounds.setInterpolator(new AnticipateOvershootInterpolator(1.0f));
TransitionManager.beginDelayedTransition(mRootView, changeBounds); TransitionManager.beginDelayedTransition(binding.rootView, changeBounds);
mConstraintSet.applyTo(mRootView); mConstraintSet.applyTo(binding.rootView);
} }
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (mIsFilterVisible) { if (mIsFilterVisible) {
showFilter(false); showFilter(false);
mTxtCurrentTool.setText(R.string.app_name); binding.txtCurrentTool.setText(R.string.app_name);
} else if (!mPhotoEditor.isCacheEmpty()) { } else if (!mPhotoEditor.isCacheEmpty()) {
showSaveDialog(); showSaveDialog();
} else { } else {

View file

@ -26,12 +26,12 @@ public class EditingToolsAdapter extends RecyclerView.Adapter<EditingToolsAdapte
public EditingToolsAdapter(OnItemSelected onItemSelected) { public EditingToolsAdapter(OnItemSelected onItemSelected) {
mOnItemSelected = onItemSelected; mOnItemSelected = onItemSelected;
mToolList.add(new ToolModel("Crop", R.drawable.ic_crop, ToolType.CROP));
mToolList.add(new ToolModel("Shape", R.drawable.ic_oval, ToolType.SHAPE)); mToolList.add(new ToolModel("Shape", R.drawable.ic_oval, ToolType.SHAPE));
mToolList.add(new ToolModel("Text", R.drawable.ic_text, ToolType.TEXT)); mToolList.add(new ToolModel("Text", R.drawable.ic_text, ToolType.TEXT));
mToolList.add(new ToolModel("Eraser", R.drawable.ic_eraser, ToolType.ERASER)); mToolList.add(new ToolModel("Eraser", R.drawable.ic_eraser, ToolType.ERASER));
mToolList.add(new ToolModel("Filter", R.drawable.ic_photo_filter, ToolType.FILTER)); mToolList.add(new ToolModel("Filter", R.drawable.ic_photo_filter, ToolType.FILTER));
mToolList.add(new ToolModel("Emoji", R.drawable.ic_insert_emoticon, ToolType.EMOJI)); mToolList.add(new ToolModel("Emoji", R.drawable.ic_insert_emoticon, ToolType.EMOJI));
mToolList.add(new ToolModel("Sticker", R.drawable.ic_sticker, ToolType.STICKER));
} }
@NonNull @NonNull

View file

@ -12,5 +12,6 @@ public enum ToolType {
ERASER, ERASER,
FILTER, FILTER,
EMOJI, EMOJI,
STICKER STICKER,
CROP
} }

View file

@ -65,6 +65,7 @@ import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
@ -439,6 +440,8 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
if ((attachment.type != null && attachment.type.toLowerCase().startsWith("image")) || (attachment.mimeType != null && attachment.mimeType.toLowerCase().startsWith("image"))) { if ((attachment.type != null && attachment.type.toLowerCase().startsWith("image")) || (attachment.mimeType != null && attachment.mimeType.toLowerCase().startsWith("image"))) {
Glide.with(composeAttachmentItemBinding.preview.getContext()) Glide.with(composeAttachmentItemBinding.preview.getContext())
.load(attachmentPath) .load(attachmentPath)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(composeAttachmentItemBinding.preview); .into(composeAttachmentItemBinding.preview);
} else if ((attachment.type != null && attachment.type.toLowerCase().startsWith("video")) || (attachment.mimeType != null && attachment.mimeType.toLowerCase().startsWith("video"))) { } else if ((attachment.type != null && attachment.type.toLowerCase().startsWith("video")) || (attachment.mimeType != null && attachment.mimeType.toLowerCase().startsWith("video"))) {
composeAttachmentItemBinding.buttonPlay.setVisibility(View.VISIBLE); composeAttachmentItemBinding.buttonPlay.setVisibility(View.VISIBLE);

View file

@ -23,8 +23,7 @@
app:layout_constraintBottom_toTopOf="@+id/rvConstraintTools" app:layout_constraintBottom_toTopOf="@+id/rvConstraintTools"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent" />
app:photo_src="@drawable/blank_image" />
<ImageView <ImageView
android:id="@+id/imgUndo" android:id="@+id/imgUndo"
@ -46,32 +45,12 @@
app:layout_constraintBottom_toTopOf="@+id/rvConstraintTools" app:layout_constraintBottom_toTopOf="@+id/rvConstraintTools"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/imgGallery"
android:layout_width="@dimen/top_tool_icon_width"
android:layout_height="wrap_content"
android:background="@color/semi_black_transparent"
android:padding="8dp"
android:src="@drawable/ic_gallery"
app:layout_constraintBottom_toTopOf="@+id/rvConstraintTools"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/imgCamera"
android:layout_width="@dimen/top_tool_icon_width"
android:layout_height="wrap_content"
android:background="@color/semi_black_transparent"
android:padding="8dp"
android:src="@drawable/ic_camera"
app:layout_constraintBottom_toTopOf="@+id/rvConstraintTools"
app:layout_constraintStart_toEndOf="@id/imgGallery" />
<ImageView <ImageView
android:id="@+id/imgClose" android:id="@+id/imgClose"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_margin="8dp" android:layout_margin="8dp"
android:src="@drawable/ic_close" android:src="@drawable/ic_close"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
@ -80,7 +59,7 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvConstraintTools" android:id="@+id/rvConstraintTools"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/tool_bg" android:background="@color/tool_bg"
android:orientation="horizontal" android:orientation="horizontal"
@ -118,26 +97,6 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/guideline" /> app:layout_constraintTop_toBottomOf="@+id/guideline" />
<ImageView
android:id="@+id/imgSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:src="@drawable/ic_save"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/guideline" />
<ImageView
android:id="@+id/imgCrop"
android:layout_width="@dimen/top_tool_icon_width"
android:layout_height="wrap_content"
android:background="@color/semi_black_transparent"
android:padding="8dp"
android:src="@drawable/ic_crop"
app:layout_constraintBottom_toBottomOf="@+id/photoEditorView"
app:layout_constraintEnd_toStartOf="@+id/imgUndo"
app:layout_constraintStart_toEndOf="@+id/imgCamera" />
<Button <Button
android:id="@+id/send" android:id="@+id/send"
@ -150,6 +109,6 @@
android:padding="0dp" android:padding="0dp"
android:text="@string/validate" android:text="@string/validate"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/imgSave" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/guideline" /> app:layout_constraintTop_toBottomOf="@+id/guideline" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>