Fix issue #225 - Set Focus on images

pull/254/head
Thomas 2 years ago
parent 6219c010d0
commit 965676484a

@ -109,12 +109,17 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
@Override
public void onReceive(android.content.Context context, Intent intent) {
String imgpath = intent.getStringExtra("imgpath");
float focusX = intent.getFloatExtra("focusX", -2);
float focusY = intent.getFloatExtra("focusY", -2);
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)) {
if (focusX != -2) {
attachment.focus = focusX + "," + focusY;
}
composeAdapter.notifyItemChanged(position);
break;
}

@ -48,6 +48,7 @@ public class Attachment implements Serializable {
public String peertubeHost = null;
public String peertubeId = null;
public String focus = null;
public static class Meta implements Serializable {
@SerializedName("focus")

@ -0,0 +1,249 @@
package app.fedilab.android.helper;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import androidx.core.content.res.ResourcesCompat;
import java.util.HashSet;
import java.util.Random;
import app.fedilab.android.R;
//Original work at https://stackoverflow.com/a/17830245
public class CirclesDrawingView extends View {
// Radius limit in pixels
private final static int RADIUS_LIMIT = 100;
private static final int CIRCLES_LIMIT = 1;
private final Random mRadiusGenerator = new Random();
/**
* All available circles
*/
private final HashSet<CircleArea> mCircles = new HashSet<>(CIRCLES_LIMIT);
private final SparseArray<CircleArea> mCirclePointer = new SparseArray<>(CIRCLES_LIMIT);
/**
* Paint to draw circles
*/
private Paint mCirclePaint;
private CircleArea touchedCircle;
/**
* Default constructor
*
* @param ct {@link android.content.Context}
*/
public CirclesDrawingView(final Context ct) {
super(ct);
init(ct);
}
public CirclesDrawingView(final Context ct, final AttributeSet attrs) {
super(ct, attrs);
init(ct);
}
public CirclesDrawingView(final Context ct, final AttributeSet attrs, final int defStyle) {
super(ct, attrs, defStyle);
init(ct);
}
public CircleArea getTouchedCircle() {
return this.touchedCircle;
}
private void init(final Context ct) {
// Generate bitmap used for background
mCirclePaint = new Paint();
mCirclePaint.setColor(ResourcesCompat.getColor(getContext().getResources(), R.color.cyanea_accent, getContext().getTheme()));
mCirclePaint.setStrokeWidth(10);
mCirclePaint.setStyle(Paint.Style.STROKE);
}
@Override
public void onDraw(final Canvas canv) {
// background bitmap to cover all area
for (CircleArea circle : mCircles) {
canv.drawCircle(circle.centerX, circle.centerY, circle.radius, mCirclePaint);
}
}
@Override
public boolean onTouchEvent(final MotionEvent event) {
boolean handled = false;
int xTouch;
int yTouch;
int pointerId;
int actionIndex = event.getActionIndex();
// get touch event coordinates and make transparent circle from it
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// it's the first pointer, so clear all existing pointers data
clearCirclePointer();
xTouch = (int) event.getX(0);
yTouch = (int) event.getY(0);
// check if we've touched inside some circle
touchedCircle = obtainTouchedCircle(xTouch, yTouch);
touchedCircle.centerX = xTouch;
touchedCircle.centerY = yTouch;
mCirclePointer.put(event.getPointerId(0), touchedCircle);
invalidate();
handled = true;
break;
case MotionEvent.ACTION_POINTER_DOWN:
// It secondary pointers, so obtain their ids and check circles
pointerId = event.getPointerId(actionIndex);
xTouch = (int) event.getX(actionIndex);
yTouch = (int) event.getY(actionIndex);
// check if we've touched inside some circle
touchedCircle = obtainTouchedCircle(xTouch, yTouch);
mCirclePointer.put(pointerId, touchedCircle);
touchedCircle.centerX = xTouch;
touchedCircle.centerY = yTouch;
invalidate();
handled = true;
break;
case MotionEvent.ACTION_MOVE:
final int pointerCount = event.getPointerCount();
for (actionIndex = 0; actionIndex < pointerCount; actionIndex++) {
// Some pointer has moved, search it by pointer id
pointerId = event.getPointerId(actionIndex);
xTouch = (int) event.getX(actionIndex);
yTouch = (int) event.getY(actionIndex);
touchedCircle = mCirclePointer.get(pointerId);
if (null != touchedCircle) {
touchedCircle.centerX = xTouch;
touchedCircle.centerY = yTouch;
}
}
invalidate();
handled = true;
break;
case MotionEvent.ACTION_UP:
clearCirclePointer();
invalidate();
handled = true;
break;
case MotionEvent.ACTION_POINTER_UP:
// not general pointer was up
pointerId = event.getPointerId(actionIndex);
mCirclePointer.remove(pointerId);
invalidate();
handled = true;
break;
case MotionEvent.ACTION_CANCEL:
handled = true;
break;
default:
// do nothing
break;
}
return super.onTouchEvent(event) || handled;
}
/**
* Clears all CircleArea - pointer id relations
*/
private void clearCirclePointer() {
mCirclePointer.clear();
}
/**
* Search and creates new (if needed) circle based on touch area
*
* @param xTouch int x of touch
* @param yTouch int y of touch
* @return obtained {@link CircleArea}
*/
private CircleArea obtainTouchedCircle(final int xTouch, final int yTouch) {
CircleArea touchedCircle = getTouchedCircle(xTouch, yTouch);
if (null == touchedCircle) {
touchedCircle = new CircleArea(xTouch, yTouch, mRadiusGenerator.nextInt(RADIUS_LIMIT) + RADIUS_LIMIT);
if (mCircles.size() == CIRCLES_LIMIT) {
// remove first circle
mCircles.clear();
}
mCircles.add(touchedCircle);
}
return touchedCircle;
}
/**
* Determines touched circle
*
* @param xTouch int x touch coordinate
* @param yTouch int y touch coordinate
* @return {@link CircleArea} touched circle or null if no circle has been touched
*/
private CircleArea getTouchedCircle(final int xTouch, final int yTouch) {
CircleArea touched = null;
for (CircleArea circle : mCircles) {
if ((circle.centerX - xTouch) * (circle.centerX - xTouch) + (circle.centerY - yTouch) * (circle.centerY - yTouch) <= circle.radius * circle.radius) {
touched = circle;
break;
}
}
return touched;
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight());
}
/**
* Stores data about single circle
*/
public static class CircleArea {
public int centerX;
public int centerY;
int radius;
CircleArea(int centerX, int centerY, int radius) {
this.radius = radius;
this.centerX = centerX;
this.centerY = centerY;
}
@Override
public String toString() {
return "Circle[" + centerX + ", " + centerY + ", " + radius + "]";
}
}
}

@ -5,6 +5,7 @@ import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
@ -33,6 +34,7 @@ import java.io.InputStream;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityEditImageBinding;
import app.fedilab.android.helper.CirclesDrawingView;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.imageeditor.base.BaseActivity;
import app.fedilab.android.imageeditor.filters.FilterListener;
@ -58,7 +60,6 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
private static final int CAMERA_REQUEST = 52;
private static final int PICK_REQUEST = 53;
private final int STORE_REQUEST = 54;
private final EditingToolsAdapter mEditingToolsAdapter = new EditingToolsAdapter(this);
private final FilterViewAdapter mFilterViewAdapter = new FilterViewAdapter(this);
private final ConstraintSet mConstraintSet = new ConstraintSet();
@ -117,8 +118,6 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
binding.rvFilterView.setLayoutManager(llmFilters);
binding.rvFilterView.setAdapter(mFilterViewAdapter);
//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");
mPhotoEditor = new PhotoEditor.Builder(this, binding.photoEditorView)
@ -246,6 +245,49 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
if (exit) {
Intent intentImage = new Intent(Helper.INTENT_SEND_MODIFIED_IMAGE);
intentImage.putExtra("imgpath", imagePath);
CirclesDrawingView.CircleArea circleArea = binding.focusCircle.getTouchedCircle();
if (circleArea != null) {
//Dimension of the editor containing the image
int pHeight = binding.photoEditorView.getHeight();
int pWidth = binding.photoEditorView.getWidth();
//Load the original image in a bitmap
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(new File(imagePath).getAbsolutePath(), options);
//Get height and width of the original image
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
//Evaluate the dimension of the image in the editor
int imgHeightInEditor;
int imgWidthInEditor;
//If the original image has its height greater than width => heights are equals
float focusX = -2, focusY = -2;
if (imageHeight > imageWidth) {
imgHeightInEditor = pHeight;
float ratio = (float) pHeight / (float) imageHeight;
imgWidthInEditor = (int) (pWidth * ratio);
} else { //Otherwise widths are equals
imgWidthInEditor = pWidth;
float ratio = (float) pWidth / (float) imageWidth;
imgHeightInEditor = (int) (pHeight * ratio);
}
focusY = (float) (circleArea.centerY * 2 - imgHeightInEditor / 2) / (float) imgHeightInEditor - 0.5f;
focusX = (float) (circleArea.centerX * 2 - imgWidthInEditor / 2) / (float) imgWidthInEditor - 0.5f;
if (focusX > 1) {
focusX = 1;
} else if (focusX < -1) {
focusX = -1;
}
if (focusY > 1) {
focusY = 1;
} else if (focusY < -1) {
focusY = -1;
}
intentImage.putExtra("focusX", focusX);
intentImage.putExtra("focusY", focusY);
}
LocalBroadcastManager.getInstance(EditImageActivity.this).sendBroadcast(intentImage);
finish();
}
@ -376,6 +418,7 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
@Override
public void onToolSelected(ToolType toolType) {
binding.focusCircle.setVisibility(View.GONE);
switch (toolType) {
case SHAPE:
mPhotoEditor.setBrushDrawingMode(true);
@ -414,6 +457,9 @@ public class EditImageActivity extends BaseActivity implements OnPhotoEditorList
CropImage.activity(uri)
.start(this);
break;
case FOCUS:
binding.focusCircle.setVisibility(View.VISIBLE);
break;
}
}

@ -32,6 +32,7 @@ public class EditingToolsAdapter extends RecyclerView.Adapter<EditingToolsAdapte
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("Emoji", R.drawable.ic_insert_emoticon, ToolType.EMOJI));
mToolList.add(new ToolModel("Focus", R.drawable.ic_baseline_filter_center_focus_24, ToolType.FOCUS));
}
@NonNull

@ -13,5 +13,6 @@ public enum ToolType {
FILTER,
EMOJI,
STICKER,
CROP
CROP,
FOCUS
}

@ -294,7 +294,8 @@ public class PostMessageService extends IntentService {
}
private static String postAttachment(MastodonStatusesService mastodonStatusesService, DataPost dataPost, MultipartBody.Part fileMultipartBody, Attachment attachment) {
Call<Attachment> attachmentCall = mastodonStatusesService.postMedia(dataPost.token, fileMultipartBody, null, attachment.description, null);
Call<Attachment> attachmentCall = mastodonStatusesService.postMedia(dataPost.token, fileMultipartBody, null, attachment.description, attachment.focus);
if (attachmentCall != null) {
try {

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M5,15L3,15v4c0,1.1 0.9,2 2,2h4v-2L5,19v-4zM5,5h4L9,3L5,3c-1.1,0 -2,0.9 -2,2v4h2L5,5zM19,3h-4v2h4v4h2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19h-4v2h4c1.1,0 2,-0.9 2,-2v-4h-2v4zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z" />
</vector>

@ -16,14 +16,27 @@
android:orientation="horizontal"
app:layout_constraintGuide_end="?attr/actionBarSize" />
<ja.burhanrashid52.photoeditor.PhotoEditorView
android:id="@+id/photoEditorView"
<RelativeLayout
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/rvConstraintTools"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
android:id="@+id/container_image"
app:layout_constraintTop_toTopOf="parent">
<ja.burhanrashid52.photoeditor.PhotoEditorView
android:id="@+id/photoEditorView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<app.fedilab.android.helper.CirclesDrawingView
android:id="@+id/focus_circle"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</RelativeLayout>
<ImageView
android:id="@+id/imgUndo"
@ -34,7 +47,6 @@
android:src="@drawable/ic_undo"
app:layout_constraintBottom_toTopOf="@+id/rvConstraintTools"
app:layout_constraintEnd_toStartOf="@+id/imgRedo" />
<ImageView
android:id="@+id/imgRedo"
android:layout_width="@dimen/top_tool_icon_width"

Loading…
Cancel
Save