From cebd7272098e7c1302201446b843088f0d33921f Mon Sep 17 00:00:00 2001 From: M66B Date: Mon, 1 Jul 2019 19:39:07 +0200 Subject: [PATCH] Fixed zooming of level drawables --- README.md | 2 +- app/build.gradle | 4 - .../eu/faircode/email/AdapterMessage.java | 3 +- .../eu/faircode/email/ZoomableImageView.java | 244 ++++++++++++++++++ 4 files changed, 246 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/eu/faircode/email/ZoomableImageView.java diff --git a/README.md b/README.md index 6d0755a342..f50b80cfa9 100644 --- a/README.md +++ b/README.md @@ -172,9 +172,9 @@ FairEmail uses: * [App shortcut icon generator](https://romannurik.github.io/AndroidAssetStudio/icons-app-shortcut.html). Copyright ???. [Apache License 2.0](https://github.com/romannurik/AndroidAssetStudio/blob/master/LICENSE). * [Mozilla ISPDB](https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration#ISPDB). *Free to use for any client.* * [ShortcutBadger](https://github.com/leolin310148/ShortcutBadger). Copyright 2014 Leo Lin. [Apache license](https://github.com/leolin310148/ShortcutBadger/blob/master/LICENSE). -* [PhotoView](https://github.com/chrisbanes/PhotoView). Copyright 2018 Chris Banes. [Apache License](https://github.com/chrisbanes/PhotoView/blob/master/LICENSE). * [Bugsnag exception reporter for Android](https://github.com/bugsnag/bugsnag-android). Copyright (c) 2012 Bugsnag. [MIT License](https://github.com/bugsnag/bugsnag-android/blob/master/LICENSE.txt). * [biweekly](https://github.com/mangstadt/biweekly). Copyright (c) 2013-2018, Michael Angstadt. [BSD 2-Clause](https://github.com/mangstadt/biweekly/blob/master/LICENSE). +* Source code snippets from Stack Overflow. [MIT License](https://meta.stackexchange.com/questions/271080/the-mit-license-clarity-on-using-code-on-stack-overflow-and-stack-exchange). Error reporting is sponsored by: diff --git a/app/build.gradle b/app/build.gradle index 7c4952a402..d2b3139634 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -145,7 +145,6 @@ dependencies { def openpgp_version = "12.0" def requery_version = "3.28.0" def badge_version = "1.1.22" - def photoview_version = "2.3.0" def bugsnag_version = "4.15.0" def biweekly_version = "0.6.3" @@ -219,9 +218,6 @@ dependencies { // https://mvnrepository.com/artifact/me.leolin/ShortcutBadger implementation "me.leolin:ShortcutBadger:$badge_version" - // https://github.com/chrisbanes/PhotoView - implementation "com.github.chrisbanes:PhotoView:$photoview_version" - // https://github.com/bugsnag/bugsnag-android implementation "com.bugsnag:bugsnag-android:$bugsnag_version" diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index 141869786d..02953b5899 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -105,7 +105,6 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.StaggeredGridLayoutManager; -import com.github.chrisbanes.photoview.PhotoView; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.LabelVisibilityMode; import com.google.android.material.snackbar.Snackbar; @@ -2094,7 +2093,7 @@ public class AdapterMessage extends RecyclerView.Adapter minScale) { + saveScale = newScale; + float width = getWidth(); + float height = getHeight(); + right = (originalBitmapWidth * saveScale) - width; + bottom = (originalBitmapHeight * saveScale) - height; + + float scaledBitmapWidth = originalBitmapWidth * saveScale; + float scaledBitmapHeight = originalBitmapHeight * saveScale; + + if (scaledBitmapWidth <= width || scaledBitmapHeight <= height) { + matrix.postScale(scaleFactor, scaleFactor, width / 2, height / 2); + } else { + matrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY()); + } + } + return true; + } + + } + + static final int NONE = 0; + static final int DRAG = 1; + static final int ZOOM = 2; + static final int CLICK = 3; + + private int mode = NONE; + + private Matrix matrix = new Matrix(); + + private PointF last = new PointF(); + private PointF start = new PointF(); + private float minScale = 0.5f; + private float maxScale = 4f; + private float[] m; + + private float redundantXSpace, redundantYSpace; + private float saveScale = 1f; + private float right, bottom, originalBitmapWidth, originalBitmapHeight; + + private ScaleGestureDetector mScaleDetector; + + public ZoomableImageView(Context context) { + super(context); + init(context); + } + + public ZoomableImageView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public ZoomableImageView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + private void init(Context context) { + super.setClickable(true); + mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); + m = new float[9]; + setImageMatrix(matrix); + setScaleType(ScaleType.MATRIX); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int bmHeight = getBmHeight(); + int bmWidth = getBmWidth(); + + float width = getMeasuredWidth(); + float height = getMeasuredHeight(); + //Fit to screen. + float scale = width > height ? height / bmHeight : width / bmWidth; + + matrix.setScale(scale, scale); + saveScale = 1f; + + originalBitmapWidth = scale * bmWidth; + originalBitmapHeight = scale * bmHeight; + + // Center the image + redundantYSpace = (height - originalBitmapHeight); + redundantXSpace = (width - originalBitmapWidth); + + matrix.postTranslate(redundantXSpace / 2, redundantYSpace / 2); + + setImageMatrix(matrix); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + mScaleDetector.onTouchEvent(event); + + matrix.getValues(m); + float x = m[Matrix.MTRANS_X]; + float y = m[Matrix.MTRANS_Y]; + PointF curr = new PointF(event.getX(), event.getY()); + + switch (event.getAction()) { + //when one finger is touching + //set the mode to DRAG + case MotionEvent.ACTION_DOWN: + last.set(event.getX(), event.getY()); + start.set(last); + mode = DRAG; + break; + //when two fingers are touching + //set the mode to ZOOM + case MotionEvent.ACTION_POINTER_DOWN: + last.set(event.getX(), event.getY()); + start.set(last); + mode = ZOOM; + break; + //when a finger moves + //If mode is applicable move image + case MotionEvent.ACTION_MOVE: + //if the mode is ZOOM or + //if the mode is DRAG and already zoomed + if (mode == ZOOM || (mode == DRAG && saveScale > minScale)) { + float deltaX = curr.x - last.x;// x difference + float deltaY = curr.y - last.y;// y difference + float scaleWidth = Math.round(originalBitmapWidth * saveScale);// width after applying current scale + float scaleHeight = Math.round(originalBitmapHeight * saveScale);// height after applying current scale + + boolean limitX = false; + boolean limitY = false; + + //if scaleWidth is smaller than the views width + //in other words if the image width fits in the view + //limit left and right movement + if (scaleWidth < getWidth() && scaleHeight < getHeight()) { + // don't do anything + } + else if (scaleWidth < getWidth()) { + deltaX = 0; + limitY = true; + } + //if scaleHeight is smaller than the views height + //in other words if the image height fits in the view + //limit up and down movement + else if (scaleHeight < getHeight()) { + deltaY = 0; + limitX = true; + } + //if the image doesnt fit in the width or height + //limit both up and down and left and right + else { + limitX = true; + limitY = true; + } + + if (limitY) { + if (y + deltaY > 0) { + deltaY = -y; + } else if (y + deltaY < -bottom) { + deltaY = -(y + bottom); + } + + } + + if (limitX) { + if (x + deltaX > 0) { + deltaX = -x; + } else if (x + deltaX < -right) { + deltaX = -(x + right); + } + + } + //move the image with the matrix + matrix.postTranslate(deltaX, deltaY); + //set the last touch location to the current + last.set(curr.x, curr.y); + } + break; + //first finger is lifted + case MotionEvent.ACTION_UP: + mode = NONE; + int xDiff = (int) Math.abs(curr.x - start.x); + int yDiff = (int) Math.abs(curr.y - start.y); + if (xDiff < CLICK && yDiff < CLICK) + performClick(); + break; + // second finger is lifted + case MotionEvent.ACTION_POINTER_UP: + mode = NONE; + break; + } + setImageMatrix(matrix); + invalidate(); + return true; + } + + public void setMaxZoom(float x) { + maxScale = x; + } + + private int getBmWidth() { + Drawable drawable = getDrawable(); + if (drawable != null) { + return drawable.getIntrinsicWidth(); + } + return 0; + } + + private int getBmHeight() { + Drawable drawable = getDrawable(); + if (drawable != null) { + return drawable.getIntrinsicHeight(); + } + return 0; + } +}