Commit 96567648 authored by Thomas's avatar Thomas
Browse files

Fix issue #225 - Set Focus on images

parent 6219c010
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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;
                            }
+1 −0
Original line number Diff line number Diff line
@@ -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")
+249 −0
Original line number Diff line number Diff line
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
+49 −3
Original line number Diff line number Diff line
@@ -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;
        }
    }

+1 −0
Original line number Diff line number Diff line
@@ -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
Loading