summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas <tschneider.ac@gmail.com>2022-07-05 19:03:05 +0200
committerThomas <tschneider.ac@gmail.com>2022-07-05 19:03:05 +0200
commit965676484ab181330385784ab61b3388de8849bc (patch)
tree500ea0d301f88de605ed5644354a54b0b4c72b32
parent6219c010d00f0c4b4ca9945adf5c64b43bca48c7 (diff)
Fix issue #225 - Set Focus on images
-rw-r--r--app/src/main/java/app/fedilab/android/activities/ComposeActivity.java5
-rw-r--r--app/src/main/java/app/fedilab/android/client/entities/api/Attachment.java1
-rw-r--r--app/src/main/java/app/fedilab/android/helper/CirclesDrawingView.java249
-rw-r--r--app/src/main/java/app/fedilab/android/imageeditor/EditImageActivity.java52
-rw-r--r--app/src/main/java/app/fedilab/android/imageeditor/tools/EditingToolsAdapter.java1
-rw-r--r--app/src/main/java/app/fedilab/android/imageeditor/tools/ToolType.java3
-rw-r--r--app/src/main/java/app/fedilab/android/services/PostMessageService.java3
-rw-r--r--app/src/main/res/drawable/ic_baseline_filter_center_focus_24.xml10
-rw-r--r--app/src/main/res/layout/activity_edit_image.xml20
9 files changed, 335 insertions, 9 deletions
diff --git a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java
index 0ddda7578..58c28c852 100644
--- a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java
+++ b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java
@@ -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;
}
diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Attachment.java b/app/src/main/java/app/fedilab/android/client/entities/api/Attachment.java
index 3359d1a20..3cd5e3c08 100644
--- a/app/src/main/java/app/fedilab/android/client/entities/api/Attachment.java
+++ b/app/src/main/java/app/fedilab/android/client/entities/api/Attachment.java
@@ -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")
diff --git a/app/src/main/java/app/fedilab/android/helper/CirclesDrawingView.java b/app/src/main/java/app/fedilab/android/helper/CirclesDrawingView.java
new file mode 100644
index 000000000..33672f60c
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/helper/CirclesDrawingView.java
@@ -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 + "]";
+ }
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/app/fedilab/android/imageeditor/EditImageActivity.java b/app/src/main/java/app/fedilab/android/imageeditor/EditImageActivity.java
index a9c68c7a5..81e650865 100644
--- a/app/src/main/java/app/fedilab/android/imageeditor/EditImageActivity.java
+++ b/app/src/main/java/app/fedilab/android/imageeditor/EditImageActivity.java
@@ -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;
}
}
diff --git a/app/src/main/java/app/fedilab/android/imageeditor/tools/EditingToolsAdapter.java b/app/src/main/java/app/fedilab/android/imageeditor/tools/EditingToolsAdapter.java
index 07c3ceb7e..5415c24cf 100644
--- a/app/src/main/java/app/fedilab/android/imageeditor/tools/EditingToolsAdapter.java
+++ b/app/src/main/java/app/fedilab/android/imageeditor/tools/EditingToolsAdapter.java
@@ -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
diff --git a/app/src/main/java/app/fedilab/android/imageeditor/tools/ToolType.java b/app/src/main/java/app/fedilab/android/imageeditor/tools/ToolType.java
index 5a48bb039..1f5a2b5cc 100644
--- a/app/src/main/java/app/fedilab/android/imageeditor/tools/ToolType.java
+++ b/app/src/main/java/app/fedilab/android/imageeditor/tools/ToolType.java
@@ -13,5 +13,6 @@ public enum ToolType {
FILTER,
EMOJI,
STICKER,
- CROP
+ CROP,
+ FOCUS
}
diff --git a/app/src/main/java/app/fedilab/android/services/PostMessageService.java b/app/src/main/java/app/fedilab/android/services/PostMessageService.java
index 736e94dbf..a4a73ab20 100644
--- a/app/src/main/java/app/fedilab/android/services/PostMessageService.java
+++ b/app/src/main/java/app/fedilab/android/services/PostMessageService.java
@@ -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 {
diff --git a/app/src/main/res/drawable/ic_baseline_filter_center_focus_24.xml b/app/src/main/res/drawable/ic_baseline_filter_center_focus_24.xml
new file mode 100644
index 000000000..757e14331
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_filter_center_focus_24.xml
@@ -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>
diff --git a/app/src/main/res/layout/activity_edit_image.xml b/app/src/main/res/layout/activity_edit_image.xml
index b671da595..8e04a7667 100644
--- a/app/src/main/res/layout/activity_edit_image.xml
+++ b/app/src/main/res/layout/activity_edit_image.xml
@@ -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"