diff --git a/lib_base/src/main/java/com/android/base/permission/AutoPermissionRequester.java b/lib_base/src/main/java/com/android/base/permission/AutoPermissionRequester.java
index b9a7ab9..d48dbd2 100644
--- a/lib_base/src/main/java/com/android/base/permission/AutoPermissionRequester.java
+++ b/lib_base/src/main/java/com/android/base/permission/AutoPermissionRequester.java
@@ -12,6 +12,7 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import timber.log.Timber;
@@ -128,11 +129,28 @@ public class AutoPermissionRequester {
FragmentManager supportFragmentManager = mActivity.getSupportFragmentManager();
AutoPermissionFragment fragment = (AutoPermissionFragment) supportFragmentManager.findFragmentByTag(AutoPermissionFragment.class.getName());
+ Lifecycle.State currentState = mActivity.getLifecycle().getCurrentState();
+
+ if (currentState.isAtLeast(Lifecycle.State.RESUMED)) {
+ startRequestInternal(supportFragmentManager, fragment);
+ } else {
+ DefaultLifecycleObserver lifecycleObserver = new DefaultLifecycleObserver() {
+ @Override
+ public void onResume(@NonNull LifecycleOwner owner) {
+ mActivity.getLifecycle().removeObserver(this);
+ startRequestInternal(supportFragmentManager, fragment);
+ }
+ };
+ mActivity.getLifecycle().addObserver(lifecycleObserver);
+ }
+ }
+
+ private void startRequestInternal(FragmentManager supportFragmentManager, AutoPermissionFragment fragment) {
if (fragment == null) {
fragment = AutoPermissionFragment.newInstance();
supportFragmentManager.beginTransaction()
.add(fragment, AutoPermissionFragment.class.getName())
- .commitNowAllowingStateLoss();
+ .commitAllowingStateLoss();
}
fragment.setRequester(getCallback());
diff --git a/lib_media_selector/README.md b/lib_media_selector/README.md
index ad362b8..e35cc58 100644
--- a/lib_media_selector/README.md
+++ b/lib_media_selector/README.md
@@ -1,6 +1,6 @@
# 多媒体文件选择库
- 目前基于boxing修改
+目前基于boxing修改
## 1 AndroidN 在 FileProvider 的 xm l配置中加入:
@@ -9,16 +9,9 @@
```
-## 2 So库
+## 2 RotatePhotoView
-```
- defaultConfig {
- ......
- ndk {//只打包armeabi架构的so库
- abiFilters 'armeabi'
- }
- }
-```
+[RotatePhotoView](https://github.com/ChenSiLiang/RotatePhotoView)
## 3 其他备选参考
@@ -29,4 +22,4 @@
- [Album](https://github.com/yanzhenjie/Album)
- [uCrop](https://github.com/Yalantis/uCrop)
- [smartCropper](https://github.com/pqpo/SmartCropper)
-- [simpleCropper](https:github.com/igreenwood/SimpleCropView)
\ No newline at end of file
+- [simpleCropper](https://github.com/igreenwood/SimpleCropView)
diff --git a/lib_media_selector/build.gradle b/lib_media_selector/build.gradle
index 6e517eb..b4daf3a 100644
--- a/lib_media_selector/build.gradle
+++ b/lib_media_selector/build.gradle
@@ -22,11 +22,19 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
+
+ sourceSets {
+ main {
+ java.srcDirs += "src/github/java"
+ res.srcDirs += "src/github/res"
+ }
+ }
+
}
dependencies {
//support
- implementation androidLibraries.compat
+ implementation androidLibraries.appcompat
implementation androidLibraries.material
/*imageLoader*/
@@ -38,17 +46,11 @@ dependencies {
implementation('com.bilibili:boxing-impl:1.0.4') {
exclude group: 'com.android.support'
+ exclude group: 'com.chensl.rotatephotoview'
}
- implementation('com.chensl.rotatephotoview:rotatephotoview:1.0.5') {
- exclude group: 'com.android.support', module: 'appcompat'
- exclude group: 'com.android.support', module: 'support-v4'
- exclude module: 'recyclerview-v7'
- }
-
- implementation('com.yalantis:ucrop:2.2.0') {
- exclude group: 'com.android.support'
- exclude group: 'com.squareup.okio'
+ implementation('com.github.yalantis:ucrop:2.2.4') {
exclude group: 'com.squareup.okhttp3'
}
-}
+
+}
\ No newline at end of file
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/Compat.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/Compat.java
new file mode 100644
index 0000000..6faccdf
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/Compat.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.view.MotionEvent;
+import android.view.View;
+
+public class Compat {
+
+ private static final int SIXTY_FPS_INTERVAL = 1000 / 60;
+
+ public static void postOnAnimation(View view, Runnable runnable) {
+ if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
+ postOnAnimationJellyBean(view, runnable);
+ } else {
+ view.postDelayed(runnable, SIXTY_FPS_INTERVAL);
+ }
+ }
+
+ @TargetApi(16)
+ private static void postOnAnimationJellyBean(View view, Runnable runnable) {
+ view.postOnAnimation(runnable);
+ }
+
+ public static int getPointerIndex(int action) {
+ if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB)
+ return getPointerIndexHoneyComb(action);
+ else
+ return getPointerIndexEclair(action);
+ }
+
+ @SuppressWarnings("deprecation")
+ @TargetApi(Build.VERSION_CODES.ECLAIR)
+ private static int getPointerIndexEclair(int action) {
+ return (action & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ private static int getPointerIndexHoneyComb(int action) {
+ return (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ }
+
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/DefaultOnDoubleTapListener.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/DefaultOnDoubleTapListener.java
new file mode 100644
index 0000000..b5a8f21
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/DefaultOnDoubleTapListener.java
@@ -0,0 +1,100 @@
+package uk.co.senab.photoview;
+
+import android.graphics.RectF;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.widget.ImageView;
+
+/**
+ * Provided default implementation of GestureDetector.OnDoubleTapListener, to be overridden with custom behavior, if needed
+ *
+ * To be used via {@link uk.co.senab.photoview.PhotoViewAttacher#setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener)}
+ */
+public class DefaultOnDoubleTapListener implements GestureDetector.OnDoubleTapListener {
+
+ private PhotoViewAttacher photoViewAttacher;
+
+ /**
+ * Default constructor
+ *
+ * @param photoViewAttacher PhotoViewAttacher to bind to
+ */
+ public DefaultOnDoubleTapListener(PhotoViewAttacher photoViewAttacher) {
+ setPhotoViewAttacher(photoViewAttacher);
+ }
+
+ /**
+ * Allows to change PhotoViewAttacher within range of single instance
+ *
+ * @param newPhotoViewAttacher PhotoViewAttacher to bind to
+ */
+ public void setPhotoViewAttacher(PhotoViewAttacher newPhotoViewAttacher) {
+ this.photoViewAttacher = newPhotoViewAttacher;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ if (this.photoViewAttacher == null)
+ return false;
+
+ ImageView imageView = photoViewAttacher.getImageView();
+
+ if (null != photoViewAttacher.getOnPhotoTapListener()) {
+ final RectF displayRect = photoViewAttacher.getDisplayRect();
+
+ if (null != displayRect) {
+ final float x = e.getX(), y = e.getY();
+
+ // Check to see if the user tapped on the photo
+ if (displayRect.contains(x, y)) {
+
+ float xResult = (x - displayRect.left)
+ / displayRect.width();
+ float yResult = (y - displayRect.top)
+ / displayRect.height();
+
+ photoViewAttacher.getOnPhotoTapListener().onPhotoTap(imageView, xResult, yResult);
+ return true;
+ }else{
+ photoViewAttacher.getOnPhotoTapListener().onOutsidePhotoTap();
+ }
+ }
+ }
+ if (null != photoViewAttacher.getOnViewTapListener()) {
+ photoViewAttacher.getOnViewTapListener().onViewTap(imageView, e.getX(), e.getY());
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent ev) {
+ if (photoViewAttacher == null)
+ return false;
+
+ try {
+ float scale = photoViewAttacher.getScale();
+ float x = ev.getX();
+ float y = ev.getY();
+
+ if (scale < photoViewAttacher.getMediumScale()) {
+ photoViewAttacher.setScale(photoViewAttacher.getMediumScale(), x, y, true);
+ } else if (scale >= photoViewAttacher.getMediumScale() && scale < photoViewAttacher.getMaximumScale()) {
+ photoViewAttacher.setScale(photoViewAttacher.getMaximumScale(), x, y, true);
+ } else {
+ photoViewAttacher.setScale(photoViewAttacher.getMinimumScale(), x, y, true);
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // Can sometimes happen when getX() and getY() is called
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onDoubleTapEvent(MotionEvent e) {
+ // Wait for the confirmed onDoubleTap() instead
+ return false;
+ }
+
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/IPhotoView.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/IPhotoView.java
new file mode 100644
index 0000000..fe72202
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/IPhotoView.java
@@ -0,0 +1,285 @@
+/**
+ * ****************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * *****************************************************************************
+ */
+package uk.co.senab.photoview;
+
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.view.GestureDetector;
+import android.view.View;
+import android.widget.ImageView;
+
+
+public interface IPhotoView {
+
+ float DEFAULT_MAX_SCALE = 3.0f;
+ float DEFAULT_MID_SCALE = 1.75f;
+ float DEFAULT_MIN_SCALE = 1.0f;
+ int DEFAULT_ZOOM_DURATION = 200;
+
+ /**
+ * Returns true if the PhotoView is set to allow zooming of Photos.
+ *
+ * @return true if the PhotoView allows zooming.
+ */
+ boolean canZoom();
+
+ /**
+ * Gets the Display Rectangle of the currently displayed Drawable. The Rectangle is relative to
+ * this View and includes all scaling and translations.
+ *
+ * @return - RectF of Displayed Drawable
+ */
+ RectF getDisplayRect();
+
+ /**
+ * Sets the Display Matrix of the currently displayed Drawable. The Rectangle is considered
+ * relative to this View and includes all scaling and translations.
+ *
+ * @param finalMatrix target matrix to set PhotoView to
+ * @return - true if rectangle was applied successfully
+ */
+ boolean setDisplayMatrix(Matrix finalMatrix);
+
+ /**
+ * Copies the Display Matrix of the currently displayed Drawable. The Rectangle is considered
+ * relative to this View and includes all scaling and translations.
+ *
+ * @param matrix target matrix to copy to
+ */
+ void getDisplayMatrix(Matrix matrix);
+
+ /**
+ * @return The current minimum scale level. What this value represents depends on the current
+ * {@link android.widget.ImageView.ScaleType}.
+ */
+ float getMinimumScale();
+
+ /**
+ * @return The current medium scale level. What this value represents depends on the current
+ * {@link android.widget.ImageView.ScaleType}.
+ */
+ float getMediumScale();
+
+ /**
+ * @return The current maximum scale level. What this value represents depends on the current
+ * {@link android.widget.ImageView.ScaleType}.
+ */
+ float getMaximumScale();
+
+ /**
+ * Returns the current scale value
+ *
+ * @return float - current scale value
+ */
+ float getScale();
+
+ /**
+ * Return the current scale type in use by the ImageView.
+ *
+ * @return current ImageView.ScaleType
+ */
+ ImageView.ScaleType getScaleType();
+
+ /**
+ * Whether to allow the ImageView's parent to intercept the touch event when the photo is scroll
+ * to it's horizontal edge.
+ *
+ * @param allow whether to allow intercepting by parent element or not
+ */
+ void setAllowParentInterceptOnEdge(boolean allow);
+
+ /**
+ * Sets the minimum scale level. What this value represents depends on the current {@link
+ * android.widget.ImageView.ScaleType}.
+ *
+ * @param minimumScale minimum allowed scale
+ */
+ void setMinimumScale(float minimumScale);
+
+ /**
+ * Sets the medium scale level. What this value represents depends on the current {@link android.widget.ImageView.ScaleType}.
+ *
+ * @param mediumScale medium scale preset
+ */
+ void setMediumScale(float mediumScale);
+
+ /**
+ * Sets the maximum scale level. What this value represents depends on the current {@link
+ * android.widget.ImageView.ScaleType}.
+ *
+ * @param maximumScale maximum allowed scale preset
+ */
+ void setMaximumScale(float maximumScale);
+
+ /**
+ * Allows to set all three scale levels at once, so you don't run into problem with setting
+ * medium/minimum scale before the maximum one
+ *
+ * @param minimumScale minimum allowed scale
+ * @param mediumScale medium allowed scale
+ * @param maximumScale maximum allowed scale preset
+ */
+ void setScaleLevels(float minimumScale, float mediumScale, float maximumScale);
+
+ /**
+ * Register a callback to be invoked when the Photo displayed by this view is long-pressed.
+ *
+ * @param listener - Listener to be registered.
+ */
+ void setOnLongClickListener(View.OnLongClickListener listener);
+
+ /**
+ * Register a callback to be invoked when the Matrix has changed for this View. An example would
+ * be the user panning or scaling the Photo.
+ *
+ * @param listener - Listener to be registered.
+ */
+ void setOnMatrixChangeListener(PhotoViewAttacher.OnMatrixChangedListener listener);
+
+ /**
+ * Register a callback to be invoked when the Photo displayed by this View is tapped with a
+ * single tap.
+ *
+ * @param listener - Listener to be registered.
+ */
+ void setOnPhotoTapListener(PhotoViewAttacher.OnPhotoTapListener listener);
+
+ /**
+ * Register a callback to be invoked when the View is tapped with a single tap.
+ *
+ * @param onRotateListener
+ */
+ void setOnRotateListener(PhotoViewAttacher.OnRotateListener onRotateListener);
+
+ /**
+ * Enables rotation via PhotoView internal functions.
+ *
+ * @param rotationDegree - Degree to rotate PhotoView to, should be in range 0 to 360
+ */
+ void setRotationTo(float rotationDegree);
+
+ /**
+ * Enables rotation via PhotoView internal functions.
+ *
+ * @param rotationDegree - Degree to rotate PhotoView by, should be in range 0 to 360
+ */
+ void setRotationBy(float rotationDegree);
+
+ /**
+ * Changes the current scale to the specified value.
+ *
+ * @param scale - Value to scale to
+ */
+ void setScale(float scale);
+
+ /**
+ * Returns a callback listener to be invoked when the View is tapped with a single tap.
+ *
+ * @return PhotoViewAttacher.OnViewTapListener currently set, may be null
+ */
+ PhotoViewAttacher.OnViewTapListener getOnViewTapListener();
+
+ /**
+ * Register a callback to be invoked when the View is tapped with a single tap.
+ *
+ * @param listener - Listener to be registered.
+ */
+ void setOnViewTapListener(PhotoViewAttacher.OnViewTapListener listener);
+
+ /**
+ * Changes the current scale to the specified value.
+ *
+ * @param scale - Value to scale to
+ * @param animate - Whether to animate the scale
+ */
+ void setScale(float scale, boolean animate);
+
+ /**
+ * Changes the current scale to the specified value, around the given focal point.
+ *
+ * @param scale - Value to scale to
+ * @param focalX - X Focus Point
+ * @param focalY - Y Focus Point
+ * @param animate - Whether to animate the scale
+ */
+ void setScale(float scale, float focalX, float focalY, boolean animate);
+
+ /**
+ * Controls how the image should be resized or moved to match the size of the ImageView. Any
+ * scaling or panning will happen within the confines of this {@link
+ * android.widget.ImageView.ScaleType}.
+ *
+ * @param scaleType - The desired scaling mode.
+ */
+ void setScaleType(ImageView.ScaleType scaleType);
+
+ /**
+ * Allows you to enable/disable the zoom functionality on the ImageView. When disable the
+ * ImageView reverts to using the FIT_CENTER matrix.
+ *
+ * @param zoomable - Whether the zoom functionality is enabled.
+ */
+ void setZoomable(boolean zoomable);
+
+ /**
+ * Extracts currently visible area to Bitmap object, if there is no image loaded yet or the
+ * ImageView is already destroyed, returns {@code null}
+ *
+ * @return currently visible area as bitmap or null
+ */
+ Bitmap getVisibleRectangleBitmap();
+
+ /**
+ * Allows to change zoom transition speed, default value is 200 (PhotoViewAttacher.DEFAULT_ZOOM_DURATION).
+ * Will default to 200 if provided negative value
+ *
+ * @param milliseconds duration of zoom interpolation
+ */
+ void setZoomTransitionDuration(int milliseconds);
+
+ /**
+ * Will return instance of IPhotoView (eg. PhotoViewAttacher), can be used to provide better
+ * integration
+ *
+ * @return IPhotoView implementation instance if available, null if not
+ */
+ IPhotoView getIPhotoViewImplementation();
+
+ /**
+ * Sets custom double tap listener, to intercept default given functions. To reset behavior to
+ * default, you can just pass in "null" or public field of PhotoViewAttacher.defaultOnDoubleTapListener
+ *
+ * @param newOnDoubleTapListener custom OnDoubleTapListener to be set on ImageView
+ */
+ void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener);
+
+ /**
+ * Will report back about scale changes
+ *
+ * @param onScaleChangeListener OnScaleChangeListener instance
+ */
+ void setOnScaleChangeListener(PhotoViewAttacher.OnScaleChangeListener onScaleChangeListener);
+
+ /**
+ * Will report back about fling(single touch)
+ *
+ * @param onSingleFlingListener OnSingleFlingListener instance
+ */
+ void setOnSingleFlingListener(PhotoViewAttacher.OnSingleFlingListener onSingleFlingListener);
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/PhotoView.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/PhotoView.java
new file mode 100644
index 0000000..5abac2e
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/PhotoView.java
@@ -0,0 +1,284 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+
+import androidx.appcompat.widget.AppCompatImageView;
+import uk.co.senab.photoview.PhotoViewAttacher.OnMatrixChangedListener;
+import uk.co.senab.photoview.PhotoViewAttacher.OnPhotoTapListener;
+import uk.co.senab.photoview.PhotoViewAttacher.OnViewTapListener;
+
+public class PhotoView extends AppCompatImageView implements IPhotoView {
+
+ private PhotoViewAttacher mAttacher;
+
+ private ScaleType mPendingScaleType;
+
+ public PhotoView(Context context) {
+ this(context, null);
+ }
+
+ public PhotoView(Context context, AttributeSet attr) {
+ this(context, attr, 0);
+ }
+
+ public PhotoView(Context context, AttributeSet attr, int defStyle) {
+ super(context, attr, defStyle);
+ super.setScaleType(ScaleType.MATRIX);
+ init();
+ }
+
+ protected void init() {
+ if (null == mAttacher || null == mAttacher.getImageView()) {
+ mAttacher = new PhotoViewAttacher(this);
+ }
+
+ if (null != mPendingScaleType) {
+ setScaleType(mPendingScaleType);
+ mPendingScaleType = null;
+ }
+ }
+
+ @Override
+ public void setRotationTo(float rotationDegree) {
+ mAttacher.setRotationTo(rotationDegree);
+ }
+
+ @Override
+ public void setRotationBy(float rotationDegree) {
+ mAttacher.setRotationBy(rotationDegree);
+ }
+
+ @Override
+ public boolean canZoom() {
+ return mAttacher.canZoom();
+ }
+
+ @Override
+ public RectF getDisplayRect() {
+ return mAttacher.getDisplayRect();
+ }
+
+ @Override
+ public void getDisplayMatrix(Matrix matrix) {
+ mAttacher.getDisplayMatrix(matrix);
+ }
+
+ @Override
+ public boolean setDisplayMatrix(Matrix finalRectangle) {
+ return mAttacher.setDisplayMatrix(finalRectangle);
+ }
+
+ @Override
+ public float getMinimumScale() {
+ return mAttacher.getMinimumScale();
+ }
+
+ @Override
+ public float getMediumScale() {
+ return mAttacher.getMediumScale();
+ }
+
+ @Override
+ public float getMaximumScale() {
+ return mAttacher.getMaximumScale();
+ }
+
+ @Override
+ public float getScale() {
+ return mAttacher.getScale();
+ }
+
+ @Override
+ public ScaleType getScaleType() {
+ return mAttacher.getScaleType();
+ }
+
+ @Override
+ public Matrix getImageMatrix() {
+ return mAttacher.getImageMatrix();
+ }
+
+ @Override
+ public void setAllowParentInterceptOnEdge(boolean allow) {
+ mAttacher.setAllowParentInterceptOnEdge(allow);
+ }
+
+ @Override
+ public void setMinimumScale(float minimumScale) {
+ mAttacher.setMinimumScale(minimumScale);
+ }
+
+ @Override
+ public void setMediumScale(float mediumScale) {
+ mAttacher.setMediumScale(mediumScale);
+ }
+
+ @Override
+ public void setMaximumScale(float maximumScale) {
+ mAttacher.setMaximumScale(maximumScale);
+ }
+
+ @Override
+ public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) {
+ mAttacher.setScaleLevels(minimumScale, mediumScale, maximumScale);
+ }
+
+ @Override
+ // setImageBitmap calls through to this method
+ public void setImageDrawable(Drawable drawable) {
+ super.setImageDrawable(drawable);
+ if (null != mAttacher) {
+ mAttacher.update();
+ }
+ }
+
+ @Override
+ public void setImageResource(int resId) {
+ super.setImageResource(resId);
+ if (null != mAttacher) {
+ mAttacher.update();
+ }
+ }
+
+ @Override
+ public void setImageURI(Uri uri) {
+ super.setImageURI(uri);
+ if (null != mAttacher) {
+ mAttacher.update();
+ }
+ }
+
+ @Override
+ protected boolean setFrame(int l, int t, int r, int b) {
+ boolean changed = super.setFrame(l, t, r, b);
+ if (null != mAttacher) {
+ mAttacher.update();
+ }
+ return changed;
+ }
+
+ @Override
+ public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
+ mAttacher.setOnMatrixChangeListener(listener);
+ }
+
+ @Override
+ public void setOnLongClickListener(OnLongClickListener l) {
+ mAttacher.setOnLongClickListener(l);
+ }
+
+ @Override
+ public void setOnPhotoTapListener(OnPhotoTapListener listener) {
+ mAttacher.setOnPhotoTapListener(listener);
+ }
+
+ @Override
+ public void setOnRotateListener(PhotoViewAttacher.OnRotateListener onRotateListener) {
+ mAttacher.setOnRotateListener(onRotateListener);
+ }
+
+ @Override
+ public OnViewTapListener getOnViewTapListener() {
+ return mAttacher.getOnViewTapListener();
+ }
+
+ @Override
+ public void setOnViewTapListener(OnViewTapListener listener) {
+ mAttacher.setOnViewTapListener(listener);
+ }
+
+ @Override
+ public void setScale(float scale) {
+ mAttacher.setScale(scale);
+ }
+
+ @Override
+ public void setScale(float scale, boolean animate) {
+ mAttacher.setScale(scale, animate);
+ }
+
+ @Override
+ public void setScale(float scale, float focalX, float focalY, boolean animate) {
+ mAttacher.setScale(scale, focalX, focalY, animate);
+ }
+
+ @Override
+ public void setScaleType(ScaleType scaleType) {
+ if (null != mAttacher) {
+ mAttacher.setScaleType(scaleType);
+ } else {
+ mPendingScaleType = scaleType;
+ }
+ }
+
+ @Override
+ public void setZoomable(boolean zoomable) {
+ mAttacher.setZoomable(zoomable);
+ }
+
+ @Override
+ public Bitmap getVisibleRectangleBitmap() {
+ return mAttacher.getVisibleRectangleBitmap();
+ }
+
+ @Override
+ public void setZoomTransitionDuration(int milliseconds) {
+ mAttacher.setZoomTransitionDuration(milliseconds);
+ }
+
+ @Override
+ public IPhotoView getIPhotoViewImplementation() {
+ return mAttacher;
+ }
+
+ @Override
+ public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) {
+ mAttacher.setOnDoubleTapListener(newOnDoubleTapListener);
+ }
+
+ @Override
+ public void setOnScaleChangeListener(PhotoViewAttacher.OnScaleChangeListener onScaleChangeListener) {
+ mAttacher.setOnScaleChangeListener(onScaleChangeListener);
+ }
+
+ @Override
+ public void setOnSingleFlingListener(PhotoViewAttacher.OnSingleFlingListener onSingleFlingListener) {
+ mAttacher.setOnSingleFlingListener(onSingleFlingListener);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ mAttacher.cleanup();
+ mAttacher = null;
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ init();
+ super.onAttachedToWindow();
+ }
+
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/PhotoViewAttacher.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/PhotoViewAttacher.java
new file mode 100644
index 0000000..42781e5
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/PhotoViewAttacher.java
@@ -0,0 +1,1373 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.Matrix.ScaleToFit;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnLongClickListener;
+import android.view.ViewParent;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+
+import java.lang.ref.WeakReference;
+
+import androidx.annotation.Nullable;
+import androidx.core.view.MotionEventCompat;
+import uk.co.senab.photoview.gestures.IRotateListener;
+import uk.co.senab.photoview.gestures.OnGestureListener;
+import uk.co.senab.photoview.gestures.RotateGestureDetector;
+import uk.co.senab.photoview.gestures.VersionedGestureDetector;
+import uk.co.senab.photoview.log.LogManager;
+import uk.co.senab.photoview.scrollerproxy.ScrollerProxy;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+
+public class PhotoViewAttacher implements IPhotoView, View.OnTouchListener,
+ OnGestureListener,
+ ViewTreeObserver.OnGlobalLayoutListener {
+
+ private static final String LOG_TAG = "PhotoViewAttacher";
+
+ // let debug flag be dynamic, but still Proguard can be used to remove from
+ // release builds
+ private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
+
+ private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
+ private int ZOOM_DURATION = DEFAULT_ZOOM_DURATION;
+
+ private static final int EDGE_NONE = -1;
+ private static final int EDGE_LEFT = 0;
+ private static final int EDGE_RIGHT = 1;
+ private static final int EDGE_BOTH = 2;
+
+ private static int SINGLE_TOUCH = 1;
+
+ private float mMinScale = DEFAULT_MIN_SCALE;
+ private float mMidScale = DEFAULT_MID_SCALE;
+ private float mMaxScale = DEFAULT_MAX_SCALE;
+
+ private boolean mAllowParentInterceptOnEdge = true;
+ private boolean mBlockParentIntercept = false;
+
+ private static void checkZoomLevels(float minZoom, float midZoom,
+ float maxZoom) {
+ if (minZoom >= midZoom) {
+ throw new IllegalArgumentException(
+ "Minimum zoom has to be less than Medium zoom. Call setMinimumZoom() with a more appropriate value");
+ } else if (midZoom >= maxZoom) {
+ throw new IllegalArgumentException(
+ "Medium zoom has to be less than Maximum zoom. Call setMaximumZoom() with a more appropriate value");
+ }
+ }
+
+ /**
+ * @return true if the ImageView exists, and it's Drawable exists
+ */
+ private static boolean hasDrawable(ImageView imageView) {
+ return null != imageView && null != imageView.getDrawable();
+ }
+
+ /**
+ * @return true if the ScaleType is supported.
+ */
+ private static boolean isSupportedScaleType(final ScaleType scaleType) {
+ if (null == scaleType) {
+ return false;
+ }
+
+ switch (scaleType) {
+ case MATRIX:
+ throw new IllegalArgumentException(scaleType.name()
+ + " is not supported in PhotoView");
+
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Set's the ImageView's ScaleType to Matrix.
+ */
+ private static void setImageViewScaleTypeMatrix(ImageView imageView) {
+ /**
+ * PhotoView sets it's own ScaleType to Matrix, then diverts all calls
+ * setScaleType to this.setScaleType automatically.
+ */
+ if (null != imageView && !(imageView instanceof IPhotoView)) {
+ if (!ScaleType.MATRIX.equals(imageView.getScaleType())) {
+ imageView.setScaleType(ScaleType.MATRIX);
+ }
+ }
+ }
+
+ private WeakReference mImageView;
+
+ // Gesture Detectors
+ private GestureDetector mGestureDetector;
+ private uk.co.senab.photoview.gestures.GestureDetector mScaleDragDetector;
+ private RotateGestureDetector mRotateGestureDetector;
+
+
+ // These are set so we don't keep allocating them on the heap
+ private final Matrix mBaseMatrix = new Matrix();
+ private final Matrix mDrawMatrix = new Matrix();
+ private final Matrix mSuppMatrix = new Matrix();
+ private final RectF mDisplayRect = new RectF();
+ private final float[] mMatrixValues = new float[9];
+
+ // Listeners
+ private OnMatrixChangedListener mMatrixChangeListener;
+ private OnPhotoTapListener mPhotoTapListener;
+ private OnViewTapListener mViewTapListener;
+ private OnLongClickListener mLongClickListener;
+ private OnScaleChangeListener mScaleChangeListener;
+ private OnRotateListener mOnRotateListener;
+ private OnSingleFlingListener mSingleFlingListener;
+
+ private int mIvTop, mIvRight, mIvBottom, mIvLeft;
+ private FlingRunnable mCurrentFlingRunnable;
+ private int mScrollEdge = EDGE_BOTH;
+ private float mBaseRotation;
+
+ private boolean mZoomEnabled;
+ private ScaleType mScaleType = ScaleType.FIT_CENTER;
+ //create by ChenSiLiang
+ private boolean mIsEnableRotate;
+ private boolean mIsToRightAngle;
+ private boolean mIsToRighting;
+ private RightAngleRunnable mRightAngleRunnable;
+
+ public PhotoViewAttacher(ImageView imageView) {
+ this(imageView, true);
+ }
+
+ public PhotoViewAttacher(ImageView imageView, boolean zoomable) {
+ mImageView = new WeakReference<>(imageView);
+
+ imageView.setDrawingCacheEnabled(true);
+ imageView.setOnTouchListener(this);
+
+ ViewTreeObserver observer = imageView.getViewTreeObserver();
+ if (null != observer)
+ observer.addOnGlobalLayoutListener(this);
+
+ // Make sure we using MATRIX Scale Type
+ setImageViewScaleTypeMatrix(imageView);
+
+ if (imageView.isInEditMode()) {
+ return;
+ }
+ // Create Gesture Detectors...
+ mScaleDragDetector = VersionedGestureDetector.newInstance(
+ imageView.getContext(), this);
+
+ mGestureDetector = new GestureDetector(imageView.getContext(),
+ new GestureDetector.SimpleOnGestureListener() {
+
+ // forward long click listener
+ @Override
+ public void onLongPress(MotionEvent e) {
+ if (null != mLongClickListener) {
+ mLongClickListener.onLongClick(getImageView());
+ }
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2,
+ float velocityX, float velocityY) {
+ if (mSingleFlingListener != null) {
+ if (getScale() > DEFAULT_MIN_SCALE) {
+ return false;
+ }
+
+ if (MotionEventCompat.getPointerCount(e1) > SINGLE_TOUCH
+ || MotionEventCompat.getPointerCount(e2) > SINGLE_TOUCH) {
+ return false;
+ }
+
+ return mSingleFlingListener.onFling(e1, e2, velocityX, velocityY);
+ }
+ return false;
+ }
+ });
+ //modify by ChenSiLiang
+ setRotateGestureDetector();
+
+ mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));
+ mBaseRotation = 0.0f;
+
+ // Finally, update the UI so that we're zoomable
+ setZoomable(zoomable);
+ }
+
+ /**
+ * set rotate
+ * Modify by ChenSL on 2015 / 9 / 16.
+ */
+ private void setRotateGestureDetector() {
+ if (mRotateGestureDetector == null) {
+ mRotateGestureDetector = new RotateGestureDetector();
+ mRotateGestureDetector.setRotateListener(new IRotateListener() {
+ @Override
+ public void rotate(int degree, int pivotX, int pivotY) {
+ if (mRightAngleRunnable != null && mIsToRighting) {
+ getImageView().removeCallbacks(mRightAngleRunnable);
+ }
+ mSuppMatrix.postRotate(degree, pivotX, pivotY);
+ if (mOnRotateListener != null) {
+ mOnRotateListener.onRotate(degree);
+ }
+ //Post the rotation to the image
+ checkAndDisplayMatrix();
+ }
+
+ @Override
+ public void upRotate(int pivotX, int pivotY) {
+ if (mIsToRightAngle) {
+ float[] v = new float[9];
+ mSuppMatrix.getValues(v);
+ // calculate the degree of rotation
+ int angle = (int) (Math.round(Math.atan2(v[Matrix.MSKEW_X], v[Matrix.MSCALE_X]) * (180 / Math.PI)));
+ if (angle <= 0) {
+ angle = -angle;
+ } else {
+ angle = 360 - angle;
+ }
+ mRightAngleRunnable = new RightAngleRunnable(angle, pivotX, pivotY);
+ getImageView().post(mRightAngleRunnable);
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) {
+ if (newOnDoubleTapListener != null) {
+ this.mGestureDetector.setOnDoubleTapListener(newOnDoubleTapListener);
+ } else {
+ this.mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));
+ }
+ }
+
+ @Override
+ public void setOnScaleChangeListener(OnScaleChangeListener onScaleChangeListener) {
+ this.mScaleChangeListener = onScaleChangeListener;
+ }
+
+ @Override
+ public void setOnSingleFlingListener(OnSingleFlingListener onSingleFlingListener) {
+ this.mSingleFlingListener = onSingleFlingListener;
+ }
+
+ /**
+ * set Rotatable
+ * Created by ChenSL on 2015/9/16.
+ *
+ * @param isRotatable true,enbale
+ */
+ public void setRotatable(boolean isRotatable) {
+ mIsEnableRotate = isRotatable;
+ }
+
+ /**
+ * set the boolean to the rotation to right angle(0,90,180,270 degree) when one finger up from the screen
+ * Created by ChenSL on 2015/9/16.
+ *
+ * @param toRightAngle true,recover to right angle when one finger lift;false,otherwise.
+ */
+ public void setToRightAngle(boolean toRightAngle) {
+ mIsToRightAngle = toRightAngle;
+ }
+
+ @Override
+ public boolean canZoom() {
+ return mZoomEnabled;
+ }
+
+ /**
+ * Clean-up the resources attached to this object. This needs to be called when the ImageView is
+ * no longer used. A good example is from {@link android.view.View#onDetachedFromWindow()} or
+ * from {@link android.app.Activity#onDestroy()}. This is automatically called if you are using
+ * {@link uk.co.senab.photoview.PhotoView}.
+ */
+ @SuppressWarnings("deprecation")
+ public void cleanup() {
+ if (null == mImageView) {
+ return; // cleanup already done
+ }
+
+ final ImageView imageView = mImageView.get();
+
+ if (null != imageView) {
+ // Remove this as a global layout listener
+ ViewTreeObserver observer = imageView.getViewTreeObserver();
+ if (null != observer && observer.isAlive()) {
+ observer.removeGlobalOnLayoutListener(this);
+ }
+
+ // Remove the ImageView's reference to this
+ imageView.setOnTouchListener(null);
+
+ // make sure a pending fling runnable won't be run
+ cancelFling();
+ }
+
+ if (null != mGestureDetector) {
+ mGestureDetector.setOnDoubleTapListener(null);
+ }
+ if (null != mRotateGestureDetector) {
+ mRotateGestureDetector.setRotateListener(null);
+ }
+
+ // Clear listeners too
+ mMatrixChangeListener = null;
+ mPhotoTapListener = null;
+ mViewTapListener = null;
+ mOnRotateListener = null;
+
+ // Finally, clear ImageView
+ mImageView = null;
+ }
+
+ @Override
+ public RectF getDisplayRect() {
+ checkMatrixBounds();
+ return getDisplayRect(getDrawMatrix());
+ }
+
+ @Override
+ public boolean setDisplayMatrix(Matrix finalMatrix) {
+ if (finalMatrix == null) {
+ throw new IllegalArgumentException("Matrix cannot be null");
+ }
+
+ ImageView imageView = getImageView();
+ if (null == imageView) {
+ return false;
+ }
+
+ if (null == imageView.getDrawable()) {
+ return false;
+ }
+
+ mSuppMatrix.set(finalMatrix);
+ setImageViewMatrix(getDrawMatrix());
+ checkMatrixBounds();
+
+ return true;
+ }
+
+ public void setBaseRotation(final float degrees) {
+ mBaseRotation = degrees % 360;
+ update();
+ setRotationBy(mBaseRotation);
+ checkAndDisplayMatrix();
+ }
+
+ @Override
+ public void setRotationTo(float degrees) {
+ mSuppMatrix.setRotate(degrees % 360);
+ checkAndDisplayMatrix();
+ }
+
+ @Override
+ public void setRotationBy(float degrees) {
+ mSuppMatrix.postRotate(degrees % 360);
+ checkAndDisplayMatrix();
+ }
+
+ public ImageView getImageView() {
+ ImageView imageView = null;
+
+ if (null != mImageView) {
+ imageView = mImageView.get();
+ }
+
+ // If we don't have an ImageView, call cleanup()
+ if (null == imageView) {
+ cleanup();
+ LogManager.getLogger().i(LOG_TAG,
+ "ImageView no longer exists. You should not use this PhotoViewAttacher any more.");
+ }
+
+ return imageView;
+ }
+
+ @Override
+ public float getMinimumScale() {
+ return mMinScale;
+ }
+
+ @Override
+ public float getMediumScale() {
+ return mMidScale;
+ }
+
+ @Override
+ public float getMaximumScale() {
+ return mMaxScale;
+ }
+
+ @Override
+ public float getScale() {
+ return (float) Math.sqrt((float) Math.pow(getValue(mSuppMatrix, Matrix.MSCALE_X), 2) + (float) Math.pow(getValue(mSuppMatrix, Matrix.MSKEW_Y), 2));
+ }
+
+ @Override
+ public ScaleType getScaleType() {
+ return mScaleType;
+ }
+
+ @Override
+ public void onDrag(float dx, float dy) {
+ if (mScaleDragDetector.isScaling()) {
+ return; // Do not drag if we are already scaling
+ }
+
+ if (DEBUG) {
+ LogManager.getLogger().d(LOG_TAG,
+ String.format("onDrag: dx: %.2f. dy: %.2f", dx, dy));
+ }
+
+ ImageView imageView = getImageView();
+ mSuppMatrix.postTranslate(dx, dy);
+ checkAndDisplayMatrix();
+
+ /**
+ * Here we decide whether to let the ImageView's parent to start taking
+ * over the touch event.
+ *
+ * First we check whether this function is enabled. We never want the
+ * parent to take over if we're scaling. We then check the edge we're
+ * on, and the direction of the scroll (i.e. if we're pulling against
+ * the edge, aka 'overscrolling', let the parent take over).
+ */
+ ViewParent parent = imageView.getParent();
+ if (mAllowParentInterceptOnEdge && !mScaleDragDetector.isScaling() && !mBlockParentIntercept) {
+ if (mScrollEdge == EDGE_BOTH
+ || (mScrollEdge == EDGE_LEFT && dx >= 1f)
+ || (mScrollEdge == EDGE_RIGHT && dx <= -1f)) {
+ if (null != parent) {
+ parent.requestDisallowInterceptTouchEvent(false);
+ }
+ }
+ } else {
+ if (null != parent) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
+ }
+ }
+
+ @Override
+ public void onFling(float startX, float startY, float velocityX,
+ float velocityY) {
+ if (DEBUG) {
+ LogManager.getLogger().d(
+ LOG_TAG,
+ "onFling. sX: " + startX + " sY: " + startY + " Vx: "
+ + velocityX + " Vy: " + velocityY);
+ }
+ ImageView imageView = getImageView();
+ mCurrentFlingRunnable = new FlingRunnable(imageView.getContext());
+ mCurrentFlingRunnable.fling(getImageViewWidth(imageView),
+ getImageViewHeight(imageView), (int) velocityX, (int) velocityY);
+ imageView.post(mCurrentFlingRunnable);
+ }
+
+ @Override
+ public void onGlobalLayout() {
+ ImageView imageView = getImageView();
+
+ if (null != imageView) {
+ if (mZoomEnabled) {
+ final int top = imageView.getTop();
+ final int right = imageView.getRight();
+ final int bottom = imageView.getBottom();
+ final int left = imageView.getLeft();
+
+ /**
+ * We need to check whether the ImageView's bounds have changed.
+ * This would be easier if we targeted API 11+ as we could just use
+ * View.OnLayoutChangeListener. Instead we have to replicate the
+ * work, keeping track of the ImageView's bounds and then checking
+ * if the values change.
+ */
+ if (top != mIvTop || bottom != mIvBottom || left != mIvLeft
+ || right != mIvRight) {
+ // Update our base matrix, as the bounds have changed
+ updateBaseMatrix(imageView.getDrawable());
+
+ // Update values as something has changed
+ mIvTop = top;
+ mIvRight = right;
+ mIvBottom = bottom;
+ mIvLeft = left;
+ }
+ } else {
+ updateBaseMatrix(imageView.getDrawable());
+ }
+ }
+ }
+
+ @Override
+ public void onScale(float scaleFactor, float focusX, float focusY) {
+ if (DEBUG) {
+ LogManager.getLogger().d(
+ LOG_TAG,
+ String.format("onScale: scale: %.2f. fX: %.2f. fY: %.2f",
+ scaleFactor, focusX, focusY));
+ }
+
+ if ((getScale() < mMaxScale || scaleFactor < 1f) && (getScale() > mMinScale || scaleFactor > 1f)) {
+ if (null != mScaleChangeListener) {
+ mScaleChangeListener.onScaleChange(scaleFactor, focusX, focusY);
+ }
+ mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
+ checkAndDisplayMatrix();
+ }
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouch(View v, MotionEvent ev) {
+ boolean handled = false;
+
+ if (mZoomEnabled && hasDrawable((ImageView) v)) {
+ ViewParent parent = v.getParent();
+ switch (ev.getAction()) {
+ case ACTION_DOWN:
+ // First, disable the Parent from intercepting the touch
+ // event
+ if (null != parent) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ } else {
+ LogManager.getLogger().i(LOG_TAG, "onTouch getParent() returned null");
+ }
+
+ // If we're flinging, and the user presses down, cancel
+ // fling
+ cancelFling();
+ break;
+
+ case ACTION_CANCEL:
+ case ACTION_UP:
+ // If the user has zoomed less than min scale, zoom back
+ // to min scale
+ if (getScale() < mMinScale) {
+ RectF rect = getDisplayRect();
+ if (null != rect) {
+ v.post(new AnimatedZoomRunnable(getScale(), mMinScale,
+ rect.centerX(), rect.centerY()));
+ handled = true;
+ }
+ }
+ break;
+ }
+
+ //detect the rotation
+ if (mIsEnableRotate && ev.getPointerCount() == 2) {
+ mRotateGestureDetector.onTouchEvent(ev);
+ }
+ boolean wasRotate = mRotateGestureDetector.isRotating();
+
+ // Try the Scale/Drag detector
+ if (null != mScaleDragDetector) {
+ boolean wasScaling = mScaleDragDetector.isScaling();
+ boolean wasDragging = mScaleDragDetector.isDragging();
+
+ handled = mScaleDragDetector.onTouchEvent(ev);
+
+ boolean didntScale = !wasScaling && !mScaleDragDetector.isScaling();
+ boolean didntDrag = !wasDragging && !mScaleDragDetector.isDragging();
+ boolean didnttRotate = !wasRotate && !mRotateGestureDetector.isRotating();
+
+ mBlockParentIntercept = didntScale && didntDrag && didnttRotate;
+ }
+
+ // Check to see if the user double tapped
+ if (null != mGestureDetector && mGestureDetector.onTouchEvent(ev)) {
+ handled = true;
+ }
+
+ }
+
+ return handled;
+ }
+
+ @Override
+ public void setAllowParentInterceptOnEdge(boolean allow) {
+ mAllowParentInterceptOnEdge = allow;
+ }
+
+ @Override
+ public void setMinimumScale(float minimumScale) {
+ checkZoomLevels(minimumScale, mMidScale, mMaxScale);
+ mMinScale = minimumScale;
+ }
+
+ @Override
+ public void setMediumScale(float mediumScale) {
+ checkZoomLevels(mMinScale, mediumScale, mMaxScale);
+ mMidScale = mediumScale;
+ }
+
+ @Override
+ public void setMaximumScale(float maximumScale) {
+ checkZoomLevels(mMinScale, mMidScale, maximumScale);
+ mMaxScale = maximumScale;
+ }
+
+ @Override
+ public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) {
+ checkZoomLevels(minimumScale, mediumScale, maximumScale);
+ mMinScale = minimumScale;
+ mMidScale = mediumScale;
+ mMaxScale = maximumScale;
+ }
+
+ @Override
+ public void setOnLongClickListener(OnLongClickListener listener) {
+ mLongClickListener = listener;
+ }
+
+ @Override
+ public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
+ mMatrixChangeListener = listener;
+ }
+
+ @Override
+ public void setOnPhotoTapListener(OnPhotoTapListener listener) {
+ mPhotoTapListener = listener;
+ }
+
+ @Override
+ public void setOnRotateListener(OnRotateListener onRotateListener) {
+ mOnRotateListener = onRotateListener;
+ }
+
+ @Override
+ public OnViewTapListener getOnViewTapListener() {
+ return mViewTapListener;
+ }
+
+ @Nullable
+ OnPhotoTapListener getOnPhotoTapListener() {
+ return mPhotoTapListener;
+ }
+
+ @Override
+ public void setOnViewTapListener(OnViewTapListener listener) {
+ mViewTapListener = listener;
+ }
+
+ @Override
+ public void setScale(float scale) {
+ setScale(scale, false);
+ }
+
+ @Override
+ public void setScale(float scale, boolean animate) {
+ ImageView imageView = getImageView();
+
+ if (null != imageView) {
+ setScale(scale,
+ (imageView.getRight()) / 2,
+ (imageView.getBottom()) / 2,
+ animate);
+ }
+ }
+
+ @Override
+ public void setScale(float scale, float focalX, float focalY,
+ boolean animate) {
+ ImageView imageView = getImageView();
+
+ if (null != imageView) {
+ // Check to see if the scale is within bounds
+ if (scale < mMinScale || scale > mMaxScale) {
+ LogManager
+ .getLogger()
+ .i(LOG_TAG,
+ "Scale must be within the range of minScale and maxScale");
+ return;
+ }
+
+ if (animate) {
+ imageView.post(new AnimatedZoomRunnable(getScale(), scale,
+ focalX, focalY));
+ } else {
+ mSuppMatrix.setScale(scale, scale, focalX, focalY);
+ checkAndDisplayMatrix();
+ }
+ }
+ }
+
+ /**
+ * Set the zoom interpolator
+ * @param interpolator the zoom interpolator
+ */
+ public void setZoomInterpolator(Interpolator interpolator) {
+ mInterpolator = interpolator;
+ }
+
+ @Override
+ public void setScaleType(ScaleType scaleType) {
+ if (isSupportedScaleType(scaleType) && scaleType != mScaleType) {
+ mScaleType = scaleType;
+
+ // Finally update
+ update();
+ }
+ }
+
+ @Override
+ public void setZoomable(boolean zoomable) {
+ mZoomEnabled = zoomable;
+ update();
+ }
+
+ public void update() {
+ ImageView imageView = getImageView();
+
+ if (null != imageView) {
+ if (mZoomEnabled) {
+ // Make sure we using MATRIX Scale Type
+ setImageViewScaleTypeMatrix(imageView);
+
+ // Update the base matrix using the current drawable
+ updateBaseMatrix(imageView.getDrawable());
+ } else {
+ // Reset the Matrix...
+ resetMatrix();
+ }
+ }
+ }
+
+ /**
+ * Get the display matrix
+ * @param matrix target matrix to copy to
+ */
+ @Override
+ public void getDisplayMatrix(Matrix matrix) {
+ matrix.set(getDrawMatrix());
+ }
+
+ /**
+ * Get the current support matrix
+ */
+ public void getSuppMatrix(Matrix matrix) {
+ matrix.set(mSuppMatrix);
+ }
+
+ private Matrix getDrawMatrix() {
+ mDrawMatrix.set(mBaseMatrix);
+ mDrawMatrix.postConcat(mSuppMatrix);
+ return mDrawMatrix;
+ }
+
+ private void cancelFling() {
+ if (null != mCurrentFlingRunnable) {
+ mCurrentFlingRunnable.cancelFling();
+ mCurrentFlingRunnable = null;
+ }
+ }
+
+ public Matrix getImageMatrix() {
+ return mDrawMatrix;
+ }
+
+ /**
+ * Helper method that simply checks the Matrix, and then displays the result
+ */
+ private void checkAndDisplayMatrix() {
+ if (checkMatrixBounds()) {
+ setImageViewMatrix(getDrawMatrix());
+ }
+ }
+
+ private void checkImageViewScaleType() {
+ ImageView imageView = getImageView();
+
+ /**
+ * PhotoView's getScaleType() will just divert to this.getScaleType() so
+ * only call if we're not attached to a PhotoView.
+ */
+ if (null != imageView && !(imageView instanceof IPhotoView)) {
+ if (!ScaleType.MATRIX.equals(imageView.getScaleType())) {
+ throw new IllegalStateException(
+ "The ImageView's ScaleType has been changed since attaching a PhotoViewAttacher. You should call setScaleType on the PhotoViewAttacher instead of on the ImageView" );
+ }
+ }
+ }
+
+ private boolean checkMatrixBounds() {
+ final ImageView imageView = getImageView();
+ if (null == imageView) {
+ return false;
+ }
+
+ final RectF rect = getDisplayRect(getDrawMatrix());
+ if (null == rect) {
+ return false;
+ }
+
+ final float height = rect.height(), width = rect.width();
+ float deltaX = 0, deltaY = 0;
+
+ final int viewHeight = getImageViewHeight(imageView);
+ if (height <= viewHeight) {
+ switch (mScaleType) {
+ case FIT_START:
+ deltaY = -rect.top;
+ break;
+ case FIT_END:
+ deltaY = viewHeight - height - rect.top;
+ break;
+ default:
+ deltaY = (viewHeight - height) / 2 - rect.top;
+ break;
+ }
+ } else if (rect.top > 0) {
+ deltaY = -rect.top;
+ } else if (rect.bottom < viewHeight) {
+ deltaY = viewHeight - rect.bottom;
+ }
+
+ final int viewWidth = getImageViewWidth(imageView);
+ if (width <= viewWidth) {
+ switch (mScaleType) {
+ case FIT_START:
+ deltaX = -rect.left;
+ break;
+ case FIT_END:
+ deltaX = viewWidth - width - rect.left;
+ break;
+ default:
+ deltaX = (viewWidth - width) / 2 - rect.left;
+ break;
+ }
+ mScrollEdge = EDGE_BOTH;
+ } else if (rect.left > 0) {
+ mScrollEdge = EDGE_LEFT;
+ deltaX = -rect.left;
+ } else if (rect.right < viewWidth) {
+ deltaX = viewWidth - rect.right;
+ mScrollEdge = EDGE_RIGHT;
+ } else {
+ mScrollEdge = EDGE_NONE;
+ }
+
+ // Finally actually translate the matrix
+ mSuppMatrix.postTranslate(deltaX, deltaY);
+
+ return true;
+ }
+
+ /**
+ * Helper method that maps the supplied Matrix to the current Drawable
+ *
+ * @param matrix - Matrix to map Drawable against
+ * @return RectF - Displayed Rectangle
+ */
+ private RectF getDisplayRect(Matrix matrix) {
+ ImageView imageView = getImageView();
+
+ if (null != imageView) {
+ Drawable d = imageView.getDrawable();
+ if (null != d) {
+ mDisplayRect.set(0, 0, d.getIntrinsicWidth(),
+ d.getIntrinsicHeight());
+ matrix.mapRect(mDisplayRect);
+ return mDisplayRect;
+ }
+ }
+ return null;
+ }
+
+ public Bitmap getVisibleRectangleBitmap() {
+ ImageView imageView = getImageView();
+ return imageView == null ? null : imageView.getDrawingCache();
+ }
+
+ @Override
+ public void setZoomTransitionDuration(int milliseconds) {
+ if (milliseconds < 0)
+ milliseconds = DEFAULT_ZOOM_DURATION;
+ this.ZOOM_DURATION = milliseconds;
+ }
+
+ @Override
+ public IPhotoView getIPhotoViewImplementation() {
+ return this;
+ }
+
+ /**
+ * Helper method that 'unpacks' a Matrix and returns the required value
+ *
+ * @param matrix - Matrix to unpack
+ * @param whichValue - Which value from Matrix.M* to return
+ * @return float - returned value
+ */
+ private float getValue(Matrix matrix, int whichValue) {
+ matrix.getValues(mMatrixValues);
+ return mMatrixValues[whichValue];
+ }
+
+ /**
+ * Resets the Matrix back to FIT_CENTER, and then displays it.s
+ */
+ private void resetMatrix() {
+ mSuppMatrix.reset();
+ setRotationBy(mBaseRotation);
+ setImageViewMatrix(getDrawMatrix());
+ checkMatrixBounds();
+ }
+
+ private void setImageViewMatrix(Matrix matrix) {
+ ImageView imageView = getImageView();
+ if (null != imageView) {
+
+ checkImageViewScaleType();
+ imageView.setImageMatrix(matrix);
+
+ // Call MatrixChangedListener if needed
+ if (null != mMatrixChangeListener) {
+ RectF displayRect = getDisplayRect(matrix);
+ if (null != displayRect) {
+ mMatrixChangeListener.onMatrixChanged(displayRect);
+ }
+ }
+ }
+ }
+
+ /**
+ * Calculate Matrix for FIT_CENTER
+ *
+ * @param d - Drawable being displayed
+ */
+ private void updateBaseMatrix(Drawable d) {
+ ImageView imageView = getImageView();
+ if (null == imageView || null == d) {
+ return;
+ }
+
+ final float viewWidth = getImageViewWidth(imageView);
+ final float viewHeight = getImageViewHeight(imageView);
+ final int drawableWidth = d.getIntrinsicWidth();
+ final int drawableHeight = d.getIntrinsicHeight();
+
+ mBaseMatrix.reset();
+
+ final float widthScale = viewWidth / drawableWidth;
+ final float heightScale = viewHeight / drawableHeight;
+
+ if (mScaleType == ScaleType.CENTER) {
+ mBaseMatrix.postTranslate((viewWidth - drawableWidth) / 2F,
+ (viewHeight - drawableHeight) / 2F);
+
+ } else if (mScaleType == ScaleType.CENTER_CROP) {
+ float scale = Math.max(widthScale, heightScale);
+ mBaseMatrix.postScale(scale, scale);
+ mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
+ (viewHeight - drawableHeight * scale) / 2F);
+
+ } else if (mScaleType == ScaleType.CENTER_INSIDE) {
+ float scale = Math.min(1.0f, Math.min(widthScale, heightScale));
+ mBaseMatrix.postScale(scale, scale);
+ mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
+ (viewHeight - drawableHeight * scale) / 2F);
+
+ } else {
+ RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight);
+ RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight);
+
+ if ((int) mBaseRotation % 180 != 0) {
+ mTempSrc = new RectF(0, 0, drawableHeight, drawableWidth);
+ }
+
+ switch (mScaleType) {
+ case FIT_CENTER:
+ mBaseMatrix
+ .setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER);
+ break;
+
+ case FIT_START:
+ mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.START);
+ break;
+
+ case FIT_END:
+ mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END);
+ break;
+
+ case FIT_XY:
+ mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.FILL);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ resetMatrix();
+ }
+
+ private int getImageViewWidth(ImageView imageView) {
+ if (null == imageView)
+ return 0;
+ return imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight();
+ }
+
+ private int getImageViewHeight(ImageView imageView) {
+ if (null == imageView)
+ return 0;
+ return imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom();
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the internal Matrix has changed for
+ * this View.
+ *
+ * @author Chris Banes
+ */
+ public interface OnMatrixChangedListener {
+ /**
+ * Callback for when the Matrix displaying the Drawable has changed. This could be because
+ * the View's bounds have changed, or the user has zoomed.
+ *
+ * @param rect - Rectangle displaying the Drawable's new bounds.
+ */
+ void onMatrixChanged(RectF rect);
+ }
+
+ /**
+ * Interface definition for callback to be invoked when attached ImageView scale changes
+ *
+ * @author Marek Sebera
+ */
+ public interface OnScaleChangeListener {
+ /**
+ * Callback for when the scale changes
+ *
+ * @param scaleFactor the scale factor (less than 1 for zoom out, greater than 1 for zoom in)
+ * @param focusX focal point X position
+ * @param focusY focal point Y position
+ */
+ void onScaleChange(float scaleFactor, float focusX, float focusY);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the Photo is tapped with a single
+ * tap.
+ *
+ * @author Chris Banes
+ */
+ public interface OnPhotoTapListener {
+
+ /**
+ * A callback to receive where the user taps on a photo. You will only receive a callback if
+ * the user taps on the actual photo, tapping on 'whitespace' will be ignored.
+ *
+ * @param view - View the user tapped.
+ * @param x - where the user tapped from the of the Drawable, as percentage of the
+ * Drawable width.
+ * @param y - where the user tapped from the top of the Drawable, as percentage of the
+ * Drawable height.
+ */
+ void onPhotoTap(View view, float x, float y);
+
+ /**
+ * A simple callback where out of photo happened;
+ * */
+ void onOutsidePhotoTap();
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the ImageView is tapped with a single
+ * tap.
+ *
+ * @author Chris Banes
+ */
+ public interface OnViewTapListener {
+
+ /**
+ * A callback to receive where the user taps on a ImageView. You will receive a callback if
+ * the user taps anywhere on the view, tapping on 'whitespace' will not be ignored.
+ *
+ * @param view - View the user tapped.
+ * @param x - where the user tapped from the left of the View.
+ * @param y - where the user tapped from the top of the View.
+ */
+ void onViewTap(View view, float x, float y);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the ImageView is roateted with two finger.
+ *
+ * @author ChenSL
+ */
+ public interface OnRotateListener {
+ /**
+ * A callBack to receive when the user rotate a ImageView.You will receive a callback
+ * if the user rotate the ImageView
+ *
+ * @param degree rotate mOldDegree
+ */
+ void onRotate(int degree);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the ImageView is fling with a single
+ * touch
+ *
+ * @author tonyjs
+ */
+ public interface OnSingleFlingListener {
+
+ /**
+ * A callback to receive where the user flings on a ImageView. You will receive a callback if
+ * the user flings anywhere on the view.
+ *
+ * @param e1 - MotionEvent the user first touch.
+ * @param e2 - MotionEvent the user last touch.
+ * @param velocityX - distance of user's horizontal fling.
+ * @param velocityY - distance of user's vertical fling.
+ */
+ boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
+ }
+
+ private class AnimatedZoomRunnable implements Runnable {
+
+ private final float mFocalX, mFocalY;
+ private final long mStartTime;
+ private final float mZoomStart, mZoomEnd;
+
+ public AnimatedZoomRunnable(final float currentZoom, final float targetZoom,
+ final float focalX, final float focalY) {
+ mFocalX = focalX;
+ mFocalY = focalY;
+ mStartTime = System.currentTimeMillis();
+ mZoomStart = currentZoom;
+ mZoomEnd = targetZoom;
+ }
+
+ @Override
+ public void run() {
+ ImageView imageView = getImageView();
+ if (imageView == null) {
+ return;
+ }
+
+ float t = interpolate();
+ float scale = mZoomStart + t * (mZoomEnd - mZoomStart);
+ float deltaScale = scale / getScale();
+
+ onScale(deltaScale, mFocalX, mFocalY);
+
+ // We haven't hit our target scale yet, so post ourselves again
+ if (t < 1f) {
+ Compat.postOnAnimation(imageView, this);
+ }
+ }
+
+ private float interpolate() {
+ float t = 1f * (System.currentTimeMillis() - mStartTime) / ZOOM_DURATION;
+ t = Math.min(1f, t);
+ t = mInterpolator.getInterpolation(t);
+ return t;
+ }
+ }
+
+ private class FlingRunnable implements Runnable {
+
+ private final ScrollerProxy mScroller;
+ private int mCurrentX, mCurrentY;
+
+ public FlingRunnable(Context context) {
+ mScroller = ScrollerProxy.getScroller(context);
+ }
+
+ public void cancelFling() {
+ if (DEBUG) {
+ LogManager.getLogger().d(LOG_TAG, "Cancel Fling");
+ }
+ mScroller.forceFinished(true);
+ }
+
+ public void fling(int viewWidth, int viewHeight, int velocityX,
+ int velocityY) {
+ final RectF rect = getDisplayRect();
+ if (null == rect) {
+ return;
+ }
+
+ final int startX = Math.round(-rect.left);
+ final int minX, maxX, minY, maxY;
+
+ if (viewWidth < rect.width()) {
+ minX = 0;
+ maxX = Math.round(rect.width() - viewWidth);
+ } else {
+ minX = maxX = startX;
+ }
+
+ final int startY = Math.round(-rect.top);
+ if (viewHeight < rect.height()) {
+ minY = 0;
+ maxY = Math.round(rect.height() - viewHeight);
+ } else {
+ minY = maxY = startY;
+ }
+
+ mCurrentX = startX;
+ mCurrentY = startY;
+
+ if (DEBUG) {
+ LogManager.getLogger().d(
+ LOG_TAG,
+ "fling. StartX:" + startX + " StartY:" + startY
+ + " MaxX:" + maxX + " MaxY:" + maxY);
+ }
+
+ // If we actually can move, fling the scroller
+ if (startX != maxX || startY != maxY) {
+ mScroller.fling(startX, startY, velocityX, velocityY, minX,
+ maxX, minY, maxY, 0, 0);
+ }
+ }
+
+ @Override
+ public void run() {
+ if (mScroller.isFinished()) {
+ return; // remaining post that should not be handled
+ }
+
+ ImageView imageView = getImageView();
+ if (null != imageView && mScroller.computeScrollOffset()) {
+
+ final int newX = mScroller.getCurrX();
+ final int newY = mScroller.getCurrY();
+
+ if (DEBUG) {
+ LogManager.getLogger().d(
+ LOG_TAG,
+ "fling run(). CurrentX:" + mCurrentX + " CurrentY:"
+ + mCurrentY + " NewX:" + newX + " NewY:"
+ + newY);
+ }
+
+ mSuppMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY);
+ setImageViewMatrix(getDrawMatrix());
+
+ mCurrentX = newX;
+ mCurrentY = newY;
+
+ // Post On animation
+ Compat.postOnAnimation(imageView, this);
+ }
+ }
+ }
+
+ /**
+ * a RightAngleRunnable that finger lift rotate to 0,90,180,270 degree
+ */
+ private class RightAngleRunnable implements Runnable {
+ private static final int RECOVER_SPEED = 4;
+ private int mOldDegree;
+ private int mNeedToRotate;
+ private int mRoPivotX;
+ private int mRoPivotY;
+
+ RightAngleRunnable(int degree, int pivotX, int pivotY) {
+ this.mOldDegree = degree;
+ this.mNeedToRotate = calDegree(degree) - mOldDegree;
+ this.mRoPivotX = pivotX;
+ this.mRoPivotY = pivotY;
+ }
+
+ /**
+ * get right degree,when one finger lifts
+ *
+ * @param oldDegree current degree
+ * @return 0, 90, 180, 270 according to oldDegree
+ */
+ private int calDegree(int oldDegree) {
+ int result;
+ float n = (float) oldDegree / 45;
+ if (n >= 0 && n < 1) {
+ result = 0;
+ } else if (n >= 1 && n <= 2.5) {
+ result = 90;
+ } else if (n > 2.5 && n < 5.5) {
+ result = 180;
+ } else if (n >= 5.5 && n <= 7) {
+ result = 270;
+ } else {
+ result = 360;
+ }
+ return result;
+ }
+
+ @Override
+ public void run() {
+ if (mNeedToRotate == 0) {
+ mIsToRighting = false;
+ return;
+ }
+ ImageView imageView = getImageView();
+ if (imageView == null) {
+ mIsToRighting = false;
+ return;
+ }
+ mIsToRighting = true;
+ if (mNeedToRotate > 0) {
+ //Clockwise rotation
+ if (mNeedToRotate >= RECOVER_SPEED) {
+ mSuppMatrix.postRotate(RECOVER_SPEED, mRoPivotX, mRoPivotY);
+ mNeedToRotate -= RECOVER_SPEED;
+ } else {
+ mSuppMatrix.postRotate(mNeedToRotate, mRoPivotX, mRoPivotY);
+ mNeedToRotate = 0;
+ }
+ } else if (mNeedToRotate < 0) {
+ //Counterclockwise rotation
+ if (mNeedToRotate <= -RECOVER_SPEED) {
+ mSuppMatrix.postRotate(-RECOVER_SPEED, mRoPivotX, mRoPivotY);
+ mNeedToRotate += RECOVER_SPEED;
+ } else {
+ mSuppMatrix.postRotate(mNeedToRotate, mRoPivotX, mRoPivotY);
+ mNeedToRotate = 0;
+ }
+ }
+ checkAndDisplayMatrix();
+ Compat.postOnAnimation(imageView, this);
+ }
+ }
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/CupcakeGestureDetector.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/CupcakeGestureDetector.java
new file mode 100644
index 0000000..e14f463
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/CupcakeGestureDetector.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview.gestures;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+
+import uk.co.senab.photoview.log.LogManager;
+
+public class CupcakeGestureDetector implements GestureDetector {
+
+ protected OnGestureListener mListener;
+ private static final String LOG_TAG = "CupcakeGestureDetector";
+ float mLastTouchX;
+ float mLastTouchY;
+ final float mTouchSlop;
+ final float mMinimumVelocity;
+
+ @Override
+ public void setOnGestureListener(OnGestureListener listener) {
+ this.mListener = listener;
+ }
+
+ public CupcakeGestureDetector(Context context) {
+ final ViewConfiguration configuration = ViewConfiguration
+ .get(context);
+ mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
+ mTouchSlop = configuration.getScaledTouchSlop();
+ }
+
+ private VelocityTracker mVelocityTracker;
+ private boolean mIsDragging;
+
+ float getActiveX(MotionEvent ev) {
+ return ev.getX();
+ }
+
+ float getActiveY(MotionEvent ev) {
+ return ev.getY();
+ }
+
+ @Override
+ public boolean isScaling() {
+ return false;
+ }
+
+ @Override
+ public boolean isDragging() {
+ return mIsDragging;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN: {
+ mVelocityTracker = VelocityTracker.obtain();
+ if (null != mVelocityTracker) {
+ mVelocityTracker.addMovement(ev);
+ } else {
+ LogManager.getLogger().i(LOG_TAG, "Velocity tracker is null");
+ }
+
+ mLastTouchX = getActiveX(ev);
+ mLastTouchY = getActiveY(ev);
+ mIsDragging = false;
+ break;
+ }
+
+ case MotionEvent.ACTION_MOVE: {
+ final float x = getActiveX(ev);
+ final float y = getActiveY(ev);
+ final float dx = x - mLastTouchX, dy = y - mLastTouchY;
+
+ if (!mIsDragging) {
+ // Use Pythagoras to see if drag length is larger than
+ // touch slop
+ mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
+ }
+
+ if (mIsDragging) {
+ mListener.onDrag(dx, dy);
+ mLastTouchX = x;
+ mLastTouchY = y;
+
+ if (null != mVelocityTracker) {
+ mVelocityTracker.addMovement(ev);
+ }
+ }
+ break;
+ }
+
+ case MotionEvent.ACTION_CANCEL: {
+ // Recycle Velocity Tracker
+ if (null != mVelocityTracker) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ break;
+ }
+
+ case MotionEvent.ACTION_UP: {
+ if (mIsDragging) {
+ if (null != mVelocityTracker) {
+ mLastTouchX = getActiveX(ev);
+ mLastTouchY = getActiveY(ev);
+
+ // Compute velocity within the last 1000ms
+ mVelocityTracker.addMovement(ev);
+ mVelocityTracker.computeCurrentVelocity(1000);
+
+ final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker
+ .getYVelocity();
+
+ // If the velocity is greater than minVelocity, call
+ // listener
+ if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) {
+ mListener.onFling(mLastTouchX, mLastTouchY, -vX,
+ -vY);
+ }
+ }
+ }
+
+ // Recycle Velocity Tracker
+ if (null != mVelocityTracker) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ break;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/EclairGestureDetector.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/EclairGestureDetector.java
new file mode 100644
index 0000000..f4b4b73
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/EclairGestureDetector.java
@@ -0,0 +1,92 @@
+/**
+ * ****************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview.gestures;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.view.MotionEvent;
+
+import uk.co.senab.photoview.Compat;
+
+@TargetApi(5)
+public class EclairGestureDetector extends CupcakeGestureDetector {
+
+ private static final int INVALID_POINTER_ID = -1;
+ private int mActivePointerId = INVALID_POINTER_ID;
+ private int mActivePointerIndex = 0;
+
+ public EclairGestureDetector(Context context) {
+ super(context);
+ }
+
+ @Override
+ float getActiveX(MotionEvent ev) {
+ try {
+ return ev.getX(mActivePointerIndex);
+ } catch (Exception e) {
+ return ev.getX();
+ }
+ }
+
+ @Override
+ float getActiveY(MotionEvent ev) {
+ try {
+ return ev.getY(mActivePointerIndex);
+ } catch (Exception e) {
+ return ev.getY();
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ final int action = ev.getAction();
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN:
+ mActivePointerId = ev.getPointerId(0);
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ mActivePointerId = INVALID_POINTER_ID;
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ // Ignore deprecation, ACTION_POINTER_ID_MASK and
+ // ACTION_POINTER_ID_SHIFT has same value and are deprecated
+ // You can have either deprecation or lint target api warning
+ final int pointerIndex = Compat.getPointerIndex(ev.getAction());
+ final int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ // This was our active pointer going up. Choose a new
+ // active pointer and adjust accordingly.
+ final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+ mActivePointerId = ev.getPointerId(newPointerIndex);
+ mLastTouchX = ev.getX(newPointerIndex);
+ mLastTouchY = ev.getY(newPointerIndex);
+ }
+ break;
+ }
+
+ mActivePointerIndex = ev
+ .findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId
+ : 0);
+ try {
+ return super.onTouchEvent(ev);
+ } catch (IllegalArgumentException e) {
+ // Fix for support lib bug, happening when onDestroy is
+ return true;
+ }
+ }
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/FroyoGestureDetector.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/FroyoGestureDetector.java
new file mode 100644
index 0000000..5331704
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/FroyoGestureDetector.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview.gestures;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+
+@TargetApi(8)
+public class FroyoGestureDetector extends EclairGestureDetector {
+
+ protected final ScaleGestureDetector mDetector;
+
+ public FroyoGestureDetector(Context context) {
+ super(context);
+ ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {
+
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ float scaleFactor = detector.getScaleFactor();
+
+ if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
+ return false;
+
+ mListener.onScale(scaleFactor,
+ detector.getFocusX(), detector.getFocusY());
+ return true;
+ }
+
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ return true;
+ }
+
+ @Override
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ // NO-OP
+ }
+ };
+ mDetector = new ScaleGestureDetector(context, mScaleListener);
+ }
+
+ @Override
+ public boolean isScaling() {
+ return mDetector.isInProgress();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ try {
+ mDetector.onTouchEvent(ev);
+ return super.onTouchEvent(ev);
+ } catch (IllegalArgumentException e) {
+ // Fix for support lib bug, happening when onDestroy is
+ return true;
+ }
+ }
+
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/GestureDetector.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/GestureDetector.java
new file mode 100644
index 0000000..3c9177e
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/GestureDetector.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview.gestures;
+
+import android.view.MotionEvent;
+
+public interface GestureDetector {
+
+ boolean onTouchEvent(MotionEvent ev);
+
+ boolean isScaling();
+
+ boolean isDragging();
+
+ void setOnGestureListener(OnGestureListener listener);
+
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/IRotateDetector.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/IRotateDetector.java
new file mode 100644
index 0000000..03a70eb
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/IRotateDetector.java
@@ -0,0 +1,24 @@
+package uk.co.senab.photoview.gestures;
+
+import android.view.MotionEvent;
+
+/**
+ * Interface to detect rotation
+ * Created by ChenSL on 2015/9/16.
+ */
+public interface IRotateDetector {
+ /**
+ * handle rotation in onTouchEvent
+ *
+ * @param event The motion event.
+ * @return True if the event was handled, false otherwise.
+ */
+ boolean onTouchEvent(MotionEvent event);
+
+ /**
+ * is the Gesture Rotate
+ *
+ * @return true:rotating;false,otherwise
+ */
+ boolean isRotating();
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/IRotateListener.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/IRotateListener.java
new file mode 100644
index 0000000..96f76aa
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/IRotateListener.java
@@ -0,0 +1,20 @@
+package uk.co.senab.photoview.gestures;
+
+/**
+ * Interface for a callback for rotation
+ * Created by ChenSL on 2015/9/16.
+ */
+public interface IRotateListener {
+ /**
+ * callback for rotation
+ *
+ * @param degree degree of rotation
+ */
+ void rotate(int degree, int pivotX, int pivotY);
+
+ /**
+ * MotionEvent.ACTION_POINTER_UP happens when two finger minus to only one
+ * change the ImageView to 0,90,180,270
+ */
+ void upRotate(int pivotX, int pivotY);
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/OnGestureListener.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/OnGestureListener.java
new file mode 100644
index 0000000..4c124d1
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/OnGestureListener.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview.gestures;
+
+public interface OnGestureListener {
+
+ void onDrag(float dx, float dy);
+
+ void onFling(float startX, float startY, float velocityX,
+ float velocityY);
+
+ void onScale(float scaleFactor, float focusX, float focusY);
+
+}
\ No newline at end of file
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/RotateGestureDetector.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/RotateGestureDetector.java
new file mode 100644
index 0000000..45cc6df
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/RotateGestureDetector.java
@@ -0,0 +1,113 @@
+package uk.co.senab.photoview.gestures;
+
+import android.view.MotionEvent;
+
+/**
+ * Handle ImageView rotate event with two fingers
+ * Created by ChenSL on 2015/9/16.
+ */
+public class RotateGestureDetector implements IRotateDetector {
+ private int mLastAngle = 0;
+ private IRotateListener mListener;
+ private boolean mIsRotate;
+
+ /**
+ * set rotation listener for callback
+ *
+ * @param listener a rotation listener
+ */
+ public void setRotateListener(IRotateListener listener) {
+ this.mListener = listener;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return doRotate(event);
+ }
+
+ @Override
+ public boolean isRotating() {
+ return mIsRotate;
+ }
+
+ /**
+ * handle rotation
+ *
+ * @param ev Motion event
+ * @return always true.
+ */
+ private boolean doRotate(MotionEvent ev) {
+ if (ev.getPointerCount() != 2) {
+ return false;
+ }
+ //Calculate the angle between the two fingers
+ int pivotX = (int) (ev.getX(0) + ev.getX(1)) / 2;
+ int pivotY = (int) (ev.getY(0) + ev.getY(1)) / 2;
+ float deltaX = ev.getX(0) - ev.getX(1);
+ float deltaY = ev.getY(0) - ev.getY(1);
+ double radians = Math.atan(deltaY / deltaX);
+ //Convert to degrees
+ int degrees = (int) (radians * 180 / Math.PI);
+ /*
+ * Must use getActionMasked() for switching to pick up pointer events.
+ * These events have the pointer index encoded in them so the return
+ * from getAction() won't match the exact action constant.
+ */
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mLastAngle = degrees;
+ mIsRotate = false;
+ break;
+ case MotionEvent.ACTION_UP:
+ mIsRotate = false;
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ mLastAngle = degrees;
+ mIsRotate = false;
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_POINTER_UP:
+ mIsRotate = false;
+ upRotate(pivotX, pivotY);
+ mLastAngle = degrees;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ mIsRotate = true;
+ int degreesValue = degrees - mLastAngle;
+ if (degreesValue > 45) {
+ //Going CCW across the boundary
+ rotate(-5, pivotX, pivotY);
+ } else if (degreesValue < -45) {
+ //Going CW across the boundary
+ rotate(5, pivotX, pivotY);
+ } else {
+ //Normal rotation, rotate the difference
+ rotate(degreesValue, pivotX, pivotY);
+ }
+ //Save the current angle
+ mLastAngle = degrees;
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * to invoke the callback
+ *
+ * @param degree degree to rotate
+ */
+ private void rotate(int degree, int pivotX, int pivotY) {
+ if (mListener != null) {
+ mListener.rotate(degree, pivotX, pivotY);
+ }
+ }
+
+ /**
+ * to invoke the finger up action
+ */
+ private void upRotate(int pivotX, int pivotY) {
+ if (mListener != null) {
+ mListener.upRotate(pivotX, pivotY);
+ }
+ }
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/VersionedGestureDetector.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/VersionedGestureDetector.java
new file mode 100644
index 0000000..c1550ce
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/gestures/VersionedGestureDetector.java
@@ -0,0 +1,42 @@
+package uk.co.senab.photoview.gestures;
+
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+
+import android.content.Context;
+import android.os.Build;
+
+public final class VersionedGestureDetector {
+
+ public static GestureDetector newInstance(Context context,
+ OnGestureListener listener) {
+ final int sdkVersion = Build.VERSION.SDK_INT;
+ GestureDetector detector;
+
+ if (sdkVersion < Build.VERSION_CODES.ECLAIR) {
+ detector = new CupcakeGestureDetector(context);
+ } else if (sdkVersion < Build.VERSION_CODES.FROYO) {
+ detector = new EclairGestureDetector(context);
+ } else {
+ detector = new FroyoGestureDetector(context);
+ }
+
+ detector.setOnGestureListener(listener);
+
+ return detector;
+ }
+
+}
\ No newline at end of file
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/log/LogManager.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/log/LogManager.java
new file mode 100644
index 0000000..eefdde3
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/log/LogManager.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview.log;
+
+import android.util.Log;
+
+/**
+ * class that holds the {@link Logger} for this library, defaults to {@link LoggerDefault} to send logs to android {@link Log}
+ */
+public final class LogManager {
+
+ private static Logger logger = new LoggerDefault();
+
+ public static void setLogger(Logger newLogger) {
+ logger = newLogger;
+ }
+
+ public static Logger getLogger() {
+ return logger;
+ }
+
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/log/Logger.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/log/Logger.java
new file mode 100644
index 0000000..5324d75
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/log/Logger.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview.log;
+
+/**
+ * interface for a logger class to replace the static calls to {@link android.util.Log}
+ */
+public interface Logger {
+ /**
+ * Send a {@link android.util.Log#VERBOSE} log message.
+ *
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ */
+ int v(String tag, String msg);
+
+ /**
+ * Send a {@link android.util.Log#VERBOSE} log message and log the exception.
+ *
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ * @param tr An exception to log
+ */
+ int v(String tag, String msg, Throwable tr);
+
+ /**
+ * Send a {@link android.util.Log#DEBUG} log message.
+ *
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ */
+ int d(String tag, String msg);
+
+ /**
+ * Send a {@link android.util.Log#DEBUG} log message and log the exception.
+ *
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ * @param tr An exception to log
+ */
+ int d(String tag, String msg, Throwable tr);
+
+ /**
+ * Send an {@link android.util.Log#INFO} log message.
+ *
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ */
+ int i(String tag, String msg);
+
+ /**
+ * Send a {@link android.util.Log#INFO} log message and log the exception.
+ *
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ * @param tr An exception to log
+ */
+ int i(String tag, String msg, Throwable tr);
+
+ /**
+ * Send a {@link android.util.Log#WARN} log message.
+ *
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ */
+ int w(String tag, String msg);
+
+ /**
+ * Send a {@link android.util.Log#WARN} log message and log the exception.
+ *
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ * @param tr An exception to log
+ */
+ int w(String tag, String msg, Throwable tr);
+
+ /**
+ * Send an {@link android.util.Log#ERROR} log message.
+ *
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ */
+ int e(String tag, String msg);
+
+ /**
+ * Send a {@link android.util.Log#ERROR} log message and log the exception.
+ *
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ * @param tr An exception to log
+ */
+ int e(String tag, String msg, Throwable tr);
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/log/LoggerDefault.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/log/LoggerDefault.java
new file mode 100644
index 0000000..f827f4a
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/log/LoggerDefault.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview.log;
+
+import android.util.Log;
+
+/**
+ * Helper class to redirect {@link LogManager#logger} to {@link Log}
+ */
+public class LoggerDefault implements Logger {
+
+ @Override
+ public int v(String tag, String msg) {
+ return Log.v(tag, msg);
+ }
+
+ @Override
+ public int v(String tag, String msg, Throwable tr) {
+ return Log.v(tag, msg, tr);
+ }
+
+ @Override
+ public int d(String tag, String msg) {
+ return Log.d(tag, msg);
+ }
+
+ @Override
+ public int d(String tag, String msg, Throwable tr) {
+ return Log.d(tag, msg, tr);
+ }
+
+ @Override
+ public int i(String tag, String msg) {
+ return Log.i(tag, msg);
+ }
+
+ @Override
+ public int i(String tag, String msg, Throwable tr) {
+ return Log.i(tag, msg, tr);
+ }
+
+ @Override
+ public int w(String tag, String msg) {
+ return Log.w(tag, msg);
+ }
+
+ @Override
+ public int w(String tag, String msg, Throwable tr) {
+ return Log.w(tag, msg, tr);
+ }
+
+ @Override
+ public int e(String tag, String msg) {
+ return Log.e(tag, msg);
+ }
+
+ @Override
+ public int e(String tag, String msg, Throwable tr) {
+ return Log.e(tag, msg, tr);
+ }
+
+
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/scrollerproxy/GingerScroller.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/scrollerproxy/GingerScroller.java
new file mode 100644
index 0000000..e831ce3
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/scrollerproxy/GingerScroller.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview.scrollerproxy;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.widget.OverScroller;
+
+@TargetApi(9)
+public class GingerScroller extends ScrollerProxy {
+
+ protected final OverScroller mScroller;
+
+ public GingerScroller(Context context) {
+ mScroller = new OverScroller(context);
+ }
+
+ @Override
+ public boolean computeScrollOffset() {
+ return mScroller.computeScrollOffset();
+ }
+
+ @Override
+ public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY,
+ int overX, int overY) {
+ mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, overX, overY);
+ }
+
+ @Override
+ public void forceFinished(boolean finished) {
+ mScroller.forceFinished(finished);
+ }
+
+ @Override
+ public boolean isFinished() {
+ return mScroller.isFinished();
+ }
+
+ @Override
+ public int getCurrX() {
+ return mScroller.getCurrX();
+ }
+
+ @Override
+ public int getCurrY() {
+ return mScroller.getCurrY();
+ }
+}
\ No newline at end of file
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/scrollerproxy/IcsScroller.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/scrollerproxy/IcsScroller.java
new file mode 100644
index 0000000..10f9eab
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/scrollerproxy/IcsScroller.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview.scrollerproxy;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+
+@TargetApi(14)
+public class IcsScroller extends GingerScroller {
+
+ public IcsScroller(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean computeScrollOffset() {
+ return mScroller.computeScrollOffset();
+ }
+
+}
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/scrollerproxy/PreGingerScroller.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/scrollerproxy/PreGingerScroller.java
new file mode 100644
index 0000000..2cd91b3
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/scrollerproxy/PreGingerScroller.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview.scrollerproxy;
+
+import android.content.Context;
+import android.widget.Scroller;
+
+public class PreGingerScroller extends ScrollerProxy {
+
+ private final Scroller mScroller;
+
+ public PreGingerScroller(Context context) {
+ mScroller = new Scroller(context);
+ }
+
+ @Override
+ public boolean computeScrollOffset() {
+ return mScroller.computeScrollOffset();
+ }
+
+ @Override
+ public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY,
+ int overX, int overY) {
+ mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
+ }
+
+ @Override
+ public void forceFinished(boolean finished) {
+ mScroller.forceFinished(finished);
+ }
+
+ public boolean isFinished() {
+ return mScroller.isFinished();
+ }
+
+ @Override
+ public int getCurrX() {
+ return mScroller.getCurrX();
+ }
+
+ @Override
+ public int getCurrY() {
+ return mScroller.getCurrY();
+ }
+}
\ No newline at end of file
diff --git a/lib_media_selector/src/github/java/uk/co/senab/photoview/scrollerproxy/ScrollerProxy.java b/lib_media_selector/src/github/java/uk/co/senab/photoview/scrollerproxy/ScrollerProxy.java
new file mode 100644
index 0000000..ce39ef1
--- /dev/null
+++ b/lib_media_selector/src/github/java/uk/co/senab/photoview/scrollerproxy/ScrollerProxy.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright 2011, 2012 Chris Banes.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package uk.co.senab.photoview.scrollerproxy;
+
+import android.content.Context;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+
+public abstract class ScrollerProxy {
+
+ public static ScrollerProxy getScroller(Context context) {
+ if (VERSION.SDK_INT < VERSION_CODES.GINGERBREAD) {
+ return new PreGingerScroller(context);
+ } else if (VERSION.SDK_INT < VERSION_CODES.ICE_CREAM_SANDWICH) {
+ return new GingerScroller(context);
+ } else {
+ return new IcsScroller(context);
+ }
+ }
+
+ public abstract boolean computeScrollOffset();
+
+ public abstract void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY,
+ int maxY, int overX, int overY);
+
+ public abstract void forceFinished(boolean finished);
+
+ public abstract boolean isFinished();
+
+ public abstract int getCurrX();
+
+ public abstract int getCurrY();
+
+
+}
diff --git a/lib_media_selector/src/github/res/values/dimens.xml b/lib_media_selector/src/github/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/lib_media_selector/src/github/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/lib_media_selector/src/github/res/values/strings.xml b/lib_media_selector/src/github/res/values/strings.xml
new file mode 100644
index 0000000..b53b8b6
--- /dev/null
+++ b/lib_media_selector/src/github/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Hello world!
+
diff --git a/lib_media_selector/src/main/java/com/android/sdk/mediaselector/BoxingGlideLoader.java b/lib_media_selector/src/main/java/com/android/sdk/mediaselector/BoxingGlideLoader.java
index 79d1990..dc637b2 100644
--- a/lib_media_selector/src/main/java/com/android/sdk/mediaselector/BoxingGlideLoader.java
+++ b/lib_media_selector/src/main/java/com/android/sdk/mediaselector/BoxingGlideLoader.java
@@ -18,8 +18,6 @@
package com.android.sdk.mediaselector;
import android.graphics.Bitmap;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.widget.ImageView;
import com.bilibili.boxing.loader.IBoxingCallback;
@@ -31,6 +29,9 @@ import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.Target;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
/**
* use https://github.com/bumptech/glide as media loader.
*
@@ -78,4 +79,4 @@ final class BoxingGlideLoader implements IBoxingMediaLoader {
.into(imageView);
}
-}
+}
\ No newline at end of file
diff --git a/lib_media_selector/src/main/java/com/android/sdk/mediaselector/BoxingUcrop.java b/lib_media_selector/src/main/java/com/android/sdk/mediaselector/BoxingUcrop.java
index 2754c2d..51558a6 100644
--- a/lib_media_selector/src/main/java/com/android/sdk/mediaselector/BoxingUcrop.java
+++ b/lib_media_selector/src/main/java/com/android/sdk/mediaselector/BoxingUcrop.java
@@ -21,15 +21,16 @@ import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.support.annotation.NonNull;
-import android.support.v4.app.Fragment;
-import android.support.v4.content.ContextCompat;
import com.bilibili.boxing.loader.IBoxingCrop;
import com.bilibili.boxing.model.config.BoxingCropOption;
import com.yalantis.ucrop.UCrop;
import com.ztiany.mediaselector.R;
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+
/**
* use Ucrop(https://github.com/Yalantis/uCrop) as the implement for {@link IBoxingCrop}
*
@@ -38,22 +39,24 @@ import com.ztiany.mediaselector.R;
final class BoxingUcrop implements IBoxingCrop {
@Override
- public void onStartCrop(Context context, Fragment fragment, @NonNull BoxingCropOption cropConfig,
- @NonNull String path, int requestCode) {
+ public void onStartCrop(Context context, Fragment fragment, @NonNull BoxingCropOption cropConfig, @NonNull String path, int requestCode) {
Uri uri = new Uri.Builder()
.scheme("file")
.appendPath(path)
.build();
+
//参数
UCrop.Options crop = new UCrop.Options();
crop.setCompressionFormat(Bitmap.CompressFormat.JPEG);
crop.withMaxResultSize(cropConfig.getMaxWidth(), cropConfig.getMaxHeight());
crop.withAspectRatio(cropConfig.getAspectRatioX(), cropConfig.getAspectRatioY());
+
//颜色
int color = ContextCompat.getColor(context, R.color.boxing_colorPrimaryDark);
crop.setToolbarColor(color);
crop.setStatusBarColor(color);
+
//开始裁减
UCrop.of(uri, cropConfig.getDestination())
.withOptions(crop)
@@ -71,4 +74,5 @@ final class BoxingUcrop implements IBoxingCrop {
}
return UCrop.getOutput(data);
}
-}
+
+}
\ No newline at end of file
diff --git a/lib_media_selector/src/main/java/com/android/sdk/mediaselector/CropOptions.java b/lib_media_selector/src/main/java/com/android/sdk/mediaselector/CropOptions.java
index a6c7f2c..e0eb950 100644
--- a/lib_media_selector/src/main/java/com/android/sdk/mediaselector/CropOptions.java
+++ b/lib_media_selector/src/main/java/com/android/sdk/mediaselector/CropOptions.java
@@ -78,5 +78,4 @@ public class CropOptions implements Serializable {
return this;
}
-
-}
+}
\ No newline at end of file
diff --git a/lib_media_selector/src/main/java/com/android/sdk/mediaselector/MediaSelector.java b/lib_media_selector/src/main/java/com/android/sdk/mediaselector/MediaSelector.java
index f0fbacb..8c44422 100644
--- a/lib_media_selector/src/main/java/com/android/sdk/mediaselector/MediaSelector.java
+++ b/lib_media_selector/src/main/java/com/android/sdk/mediaselector/MediaSelector.java
@@ -4,7 +4,6 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
-import android.support.v4.app.Fragment;
import android.text.TextUtils;
import com.bilibili.boxing.Boxing;
@@ -22,6 +21,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import androidx.fragment.app.Fragment;
+
/**
* 基于 Boxing 的多媒体文件选择器
*
@@ -110,12 +111,10 @@ public class MediaSelector {
private void start(BoxingConfig boxingConfig, int requestCode) {
if (mFragment != null) {
- Boxing boxing = Boxing.of(boxingConfig)
- .withIntent(mFragment.getContext(), BoxingActivity.class);
+ Boxing boxing = Boxing.of(boxingConfig).withIntent(mFragment.getContext(), BoxingActivity.class);
boxing.start(mFragment, requestCode);
} else if (mActivity != null) {
- Boxing boxing = Boxing.of(boxingConfig)
- .withIntent(mActivity, BoxingActivity.class);
+ Boxing boxing = Boxing.of(boxingConfig).withIntent(mActivity, BoxingActivity.class);
boxing.start(mActivity, requestCode);
}
}
diff --git a/lib_media_selector/src/main/java/com/android/sdk/mediaselector/SystemMediaSelector.java b/lib_media_selector/src/main/java/com/android/sdk/mediaselector/SystemMediaSelector.java
index 9ac4037..00ba5a1 100644
--- a/lib_media_selector/src/main/java/com/android/sdk/mediaselector/SystemMediaSelector.java
+++ b/lib_media_selector/src/main/java/com/android/sdk/mediaselector/SystemMediaSelector.java
@@ -4,12 +4,13 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
-import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
+import androidx.fragment.app.Fragment;
+
/**
* 通过系统相册或者系统相机获取照片
*
diff --git a/lib_media_selector/src/main/java/com/android/sdk/mediaselector/Utils.java b/lib_media_selector/src/main/java/com/android/sdk/mediaselector/Utils.java
index 3490f06..cf9bf43 100644
--- a/lib_media_selector/src/main/java/com/android/sdk/mediaselector/Utils.java
+++ b/lib_media_selector/src/main/java/com/android/sdk/mediaselector/Utils.java
@@ -15,7 +15,6 @@ import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
-import android.support.v4.content.FileProvider;
import android.text.TextUtils;
import java.io.File;
@@ -23,6 +22,8 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
+import androidx.core.content.FileProvider;
+
/**
* See:
*
@@ -35,7 +36,6 @@ import java.util.List;
*/
final class Utils {
-
private Utils() {
throw new UnsupportedOperationException("Utils");
}
@@ -365,4 +365,5 @@ final class Utils {
// 最后通知图库更新
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + photoPath)));
}
-}
+
+}
\ No newline at end of file
diff --git a/lib_qrcode/README.md b/lib_qrcode/README.md
index 3ee2da5..e5598d9 100644
--- a/lib_qrcode/README.md
+++ b/lib_qrcode/README.md
@@ -1,3 +1,3 @@
-## 二维码扫描库
+# 二维码扫描库
修改自 [BGAQRCode-Android](https://github.com/bingoogolapple/BGAQRCode-Android)
\ No newline at end of file
diff --git a/lib_qrcode/build.gradle b/lib_qrcode/build.gradle
index 4163ab4..e386156 100644
--- a/lib_qrcode/build.gradle
+++ b/lib_qrcode/build.gradle
@@ -35,6 +35,6 @@ android {
dependencies {
implementation 'com.google.zxing:core:3.3.3'
api fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
- /*implementation uiLibraries.fotoapparat*/
+ implementation uiLibraries.fotoapparat
implementation kotlinLibraries.kotlinStdlib
}
\ No newline at end of file
diff --git a/lib_qrcode/libs/fotoapparat.aar b/lib_qrcode/libs/fotoapparat.aar
deleted file mode 100644
index 17f988c..0000000
Binary files a/lib_qrcode/libs/fotoapparat.aar and /dev/null differ
diff --git a/lib_qrcode/src/main/java/com/android/sdk/qrcode/CameraUtils.java b/lib_qrcode/src/main/java/com/android/sdk/qrcode/CameraUtils.java
index 58ea035..54aa372 100644
--- a/lib_qrcode/src/main/java/com/android/sdk/qrcode/CameraUtils.java
+++ b/lib_qrcode/src/main/java/com/android/sdk/qrcode/CameraUtils.java
@@ -13,8 +13,7 @@ import java.util.List;
import io.fotoapparat.parameter.Resolution;
-@SuppressWarnings("unused")
-final class CameraUtils {
+public class CameraUtils {
private static final double MAX_ASPECT_DISTORTION = 0.15;//最大比例偏差
private static final int MIN_PREVIEW_PIXELS = 480 * 800;//小于此预览尺寸直接移除
@@ -26,7 +25,7 @@ final class CameraUtils {
}
}
- static Resolution findBestPictureSize(Context context, Collection collection) {
+ public static Resolution findBestPictureSize(Context context, Collection collection) {
if (collection.isEmpty()) {
return null;
}
@@ -36,7 +35,7 @@ final class CameraUtils {
return bestPictureResolution;
}
- static Resolution findBestPreviewSize(Context context, Collection collection) {
+ public static Resolution findBestPreviewSize(Context context, Collection collection) {
if (collection.isEmpty()) {
return null;
}
diff --git a/lib_qrcode/src/main/java/com/android/sdk/qrcode/zxing/QRCodeDecoder.java b/lib_qrcode/src/main/java/com/android/sdk/qrcode/QRCodeDecoder.java
similarity index 98%
rename from lib_qrcode/src/main/java/com/android/sdk/qrcode/zxing/QRCodeDecoder.java
rename to lib_qrcode/src/main/java/com/android/sdk/qrcode/QRCodeDecoder.java
index f7ea79b..af56d03 100644
--- a/lib_qrcode/src/main/java/com/android/sdk/qrcode/zxing/QRCodeDecoder.java
+++ b/lib_qrcode/src/main/java/com/android/sdk/qrcode/QRCodeDecoder.java
@@ -1,4 +1,4 @@
-package com.android.sdk.qrcode.zxing;
+package com.android.sdk.qrcode;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
diff --git a/lib_qrcode/src/main/java/com/android/sdk/qrcode/zxing/QRCodeEncoder.java b/lib_qrcode/src/main/java/com/android/sdk/qrcode/QRCodeEncoder.java
similarity index 99%
rename from lib_qrcode/src/main/java/com/android/sdk/qrcode/zxing/QRCodeEncoder.java
rename to lib_qrcode/src/main/java/com/android/sdk/qrcode/QRCodeEncoder.java
index 1033ba5..dfead74 100644
--- a/lib_qrcode/src/main/java/com/android/sdk/qrcode/zxing/QRCodeEncoder.java
+++ b/lib_qrcode/src/main/java/com/android/sdk/qrcode/QRCodeEncoder.java
@@ -1,4 +1,4 @@
-package com.android.sdk.qrcode.zxing;
+package com.android.sdk.qrcode;
import android.graphics.Bitmap;
import android.graphics.Canvas;
diff --git a/lib_qrcode/src/main/java/com/android/sdk/qrcode/QRCodeView.kt b/lib_qrcode/src/main/java/com/android/sdk/qrcode/QRCodeView.kt
index 249e49d..e82ad9b 100644
--- a/lib_qrcode/src/main/java/com/android/sdk/qrcode/QRCodeView.kt
+++ b/lib_qrcode/src/main/java/com/android/sdk/qrcode/QRCodeView.kt
@@ -18,14 +18,14 @@ abstract class QRCodeView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr), ProcessDataTask.Delegate {
- private lateinit var mCameraView: CameraView
- private lateinit var mFotoapparat: Fotoapparat
+ private lateinit var cameraView: CameraView
+ private lateinit var fotoapparat: Fotoapparat
- private lateinit var mScanBoxView: ScanBoxView
- protected var mDelegate: Delegate? = null
+ private lateinit var scanBoxView: ScanBoxView
+ private var delegate: Delegate? = null
- private var mProcessDataTask: ProcessDataTask? = null
- protected var mSpotAble = false
+ private var dataTask: ProcessDataTask? = null
+ protected var spotAble = false
private val framingRect = Rect()
private var framingRectInPreview: Rect? = null
@@ -35,16 +35,16 @@ abstract class QRCodeView @JvmOverloads constructor(
}
private fun initView(context: Context, attrs: AttributeSet?) {
- mCameraView = CameraView(getContext())
- mScanBoxView = ScanBoxView(getContext())
- mScanBoxView.initCustomAttrs(context, attrs)
- addView(mCameraView)
- addView(mScanBoxView)
+ cameraView = CameraView(getContext())
+ scanBoxView = ScanBoxView(getContext())
+ scanBoxView.initCustomAttrs(context, attrs)
+ addView(cameraView)
+ addView(scanBoxView)
try {
- mFotoapparat = createFotoapparat()
+ fotoapparat = createFotoapparat()
} catch (e: Exception) {
e.printStackTrace()
- mDelegate?.onScanQRCodeOpenCameraError(e)
+ delegate?.onScanQRCodeOpenCameraError(e)
}
}
@@ -65,27 +65,27 @@ abstract class QRCodeView @JvmOverloads constructor(
return Fotoapparat(
context = this@QRCodeView.context,
- view = mCameraView,
+ view = cameraView,
logger = logcat(),
lensPosition = back(),
cameraConfiguration = configuration,
cameraErrorCallback = {
- mDelegate?.onScanQRCodeOpenCameraError(it)
+ delegate?.onScanQRCodeOpenCameraError(it)
}
)
}
private fun processFrame(frame: Frame) {
- val processDataTask = mProcessDataTask
- if (mSpotAble && (processDataTask == null || processDataTask.isCancelled)) {
+ val processDataTask = dataTask
+ if (spotAble && (processDataTask == null || processDataTask.isCancelled)) {
- mProcessDataTask = object : ProcessDataTask(frame.image, frame.size, frame.rotation, this) {
+ dataTask = object : ProcessDataTask(frame.image, frame.size, frame.rotation, this) {
override fun onPostExecute(result: String?) {
- if (mSpotAble) {
+ if (spotAble) {
if (!result.isNullOrEmpty()) {
try {
- mDelegate?.onScanQRCodeSuccess(result)
+ delegate?.onScanQRCodeSuccess(result)
stopSpot()
} catch (e: Exception) {
e.printStackTrace()
@@ -105,21 +105,21 @@ abstract class QRCodeView @JvmOverloads constructor(
* @param delegate 扫描二维码的代理
*/
fun setDelegate(delegate: Delegate) {
- mDelegate = delegate
+ this.delegate = delegate
}
/**
* 显示扫描框
*/
fun showScanRect() {
- mScanBoxView.visibility = View.VISIBLE
+ scanBoxView.visibility = View.VISIBLE
}
/**
* 隐藏扫描框
*/
fun hiddenScanRect() {
- mScanBoxView.visibility = View.GONE
+ scanBoxView.visibility = View.GONE
}
/**
@@ -127,7 +127,7 @@ abstract class QRCodeView @JvmOverloads constructor(
*/
fun startCamera() {
try {
- mFotoapparat.start()
+ fotoapparat.start()
} catch (throwable: Throwable) {
throwable.printStackTrace()
}
@@ -140,7 +140,7 @@ abstract class QRCodeView @JvmOverloads constructor(
fun stopCamera() {
stopSpotAndHiddenRect()
try {
- mFotoapparat.stop()
+ fotoapparat.stop()
} catch (throwable: Throwable) {
throwable.printStackTrace()
}
@@ -151,7 +151,7 @@ abstract class QRCodeView @JvmOverloads constructor(
*/
fun startSpot() {
postDelayed({
- mSpotAble = true
+ spotAble = true
startCamera()
}, 100)
}
@@ -161,7 +161,7 @@ abstract class QRCodeView @JvmOverloads constructor(
*/
fun stopSpot() {
cancelProcessDataTask()
- mSpotAble = false
+ spotAble = false
}
/**
@@ -186,14 +186,14 @@ abstract class QRCodeView @JvmOverloads constructor(
* @return
*/
val isScanBarcodeStyle: Boolean
- get() = mScanBoxView.isBarcode
+ get() = scanBoxView.isBarcode
/**
* 打开闪光灯
*/
fun openFlashlight() {
try {
- mFotoapparat.updateConfiguration(UpdateConfiguration(flashMode = firstAvailable(torch(), off())))
+ fotoapparat.updateConfiguration(UpdateConfiguration(flashMode = firstAvailable(torch(), off())))
} catch (e: Exception) {
e.printStackTrace()
}
@@ -205,7 +205,7 @@ abstract class QRCodeView @JvmOverloads constructor(
*/
fun closeFlashlight() {
try {
- mFotoapparat.updateConfiguration(UpdateConfiguration(flashMode = firstAvailable(off())))
+ fotoapparat.updateConfiguration(UpdateConfiguration(flashMode = firstAvailable(off())))
} catch (e: Exception) {
e.printStackTrace()
}
@@ -216,29 +216,29 @@ abstract class QRCodeView @JvmOverloads constructor(
* 销毁二维码扫描控件
*/
fun onDestroy() {
- mDelegate = null
+ delegate = null
}
/**
* 取消数据处理任务
*/
protected fun cancelProcessDataTask() {
- mProcessDataTask?.cancelTask()
- mProcessDataTask = null
+ dataTask?.cancelTask()
+ dataTask = null
}
/**
* 切换成扫描条码样式
*/
fun changeToScanBarcodeStyle() {
- mScanBoxView.isBarcode = true
+ scanBoxView.isBarcode = true
}
/**
* 切换成扫描二维码样式
*/
fun changeToScanQRCodeStyle() {
- mScanBoxView.isBarcode = false
+ scanBoxView.isBarcode = false
}
fun setDebug(debug: Boolean) {
@@ -246,7 +246,7 @@ abstract class QRCodeView @JvmOverloads constructor(
}
protected fun getFramingRectInPreview(previewWidth: Int, previewHeight: Int): Rect? {
- if (!mScanBoxView.getScanBoxAreaRect(framingRect)) {
+ if (!scanBoxView.getScanBoxAreaRect(framingRect)) {
return null
}
if (framingRectInPreview == null) {
@@ -274,7 +274,7 @@ abstract class QRCodeView @JvmOverloads constructor(
/**
* 处理打开相机出错
*/
- fun onScanQRCodeOpenCameraError(error: java.lang.Exception)
+ fun onScanQRCodeOpenCameraError(error: Exception)
}
diff --git a/lib_qrcode/src/main/java/com/android/sdk/qrcode/zxing/ZXingView.java b/lib_qrcode/src/main/java/com/android/sdk/qrcode/ZXingView.java
similarity index 94%
rename from lib_qrcode/src/main/java/com/android/sdk/qrcode/zxing/ZXingView.java
rename to lib_qrcode/src/main/java/com/android/sdk/qrcode/ZXingView.java
index 254c02d..54d4413 100644
--- a/lib_qrcode/src/main/java/com/android/sdk/qrcode/zxing/ZXingView.java
+++ b/lib_qrcode/src/main/java/com/android/sdk/qrcode/ZXingView.java
@@ -1,11 +1,9 @@
-package com.android.sdk.qrcode.zxing;
+package com.android.sdk.qrcode;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
-import com.android.sdk.qrcode.QRCodeView;
-import com.android.sdk.qrcode.Debug;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.PlanarYUVLuminanceSource;