td> 654
+    }
655
+
656
+    @Override
657
+    public void setOnLongClickListener(OnLongClickListener listener) {
658
+        mLongClickListener = listener;
659
+    }
660
+
661
+    @Override
662
+    public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
663
+        mMatrixChangeListener = listener;
664
+    }
665
+
666
+    @Override
667
+    public void setOnPhotoTapListener(OnPhotoTapListener listener) {
668
+        mPhotoTapListener = listener;
669
+    }
670
+
671
+    @Override
672
+    public void setOnRotateListener(OnRotateListener onRotateListener) {
673
+        mOnRotateListener = onRotateListener;
674
+    }
675
+
676
+    @Override
677
+    public OnViewTapListener getOnViewTapListener() {
678
+        return mViewTapListener;
679
+    }
680
+
681
+    @Nullable
682
+    OnPhotoTapListener getOnPhotoTapListener() {
683
+        return mPhotoTapListener;
684
+    }
685
+
686
+    @Override
687
+    public void setOnViewTapListener(OnViewTapListener listener) {
688
+        mViewTapListener = listener;
689
+    }
690
+
691
+    @Override
692
+    public void setScale(float scale) {
693
+        setScale(scale, false);
694
+    }
695
+
696
+    @Override
697
+    public void setScale(float scale, boolean animate) {
698
+        ImageView imageView = getImageView();
699
+
700
+        if (null != imageView) {
701
+            setScale(scale,
702
+                    (imageView.getRight()) / 2,
703
+                    (imageView.getBottom()) / 2,
704
+                    animate);
705
+        }
706
+    }
707
+
708
+    @Override
709
+    public void setScale(float scale, float focalX, float focalY,
710
+                         boolean animate) {
711
+        ImageView imageView = getImageView();
712
+
713
+        if (null != imageView) {
714
+            // Check to see if the scale is within bounds
715
+            if (scale < mMinScale || scale > mMaxScale) {
716
+                LogManager
717
+                        .getLogger()
718
+                        .i(LOG_TAG,
719
+                                "Scale must be within the range of minScale and maxScale");
720
+                return;
721
+            }
722
+
723
+            if (animate) {
724
+                imageView.post(new AnimatedZoomRunnable(getScale(), scale,
725
+                        focalX, focalY));
726
+            } else {
727
+                mSuppMatrix.setScale(scale, scale, focalX, focalY);
728
+                checkAndDisplayMatrix();
729
+            }
730
+        }
731
+    }
732
+
733
+    /**
734
+     * Set the zoom interpolator
735
+     * @param interpolator the zoom interpolator
736
+     */
737
+    public void setZoomInterpolator(Interpolator interpolator) {
738
+        mInterpolator = interpolator;
739
+    }
740
+
741
+    @Override
742
+    public void setScaleType(ScaleType scaleType) {
743
+        if (isSupportedScaleType(scaleType) && scaleType != mScaleType) {
744
+            mScaleType = scaleType;
745
+
746
+            // Finally update
747
+            update();
748
+        }
749
+    }
750
+
751
+    @Override
752
+    public void setZoomable(boolean zoomable) {
753
+        mZoomEnabled = zoomable;
754
+        update();
755
+    }
756
+
757
+    public void update() {
758
+        ImageView imageView = getImageView();
759
+
760
+        if (null != imageView) {
761
+            if (mZoomEnabled) {
762
+                // Make sure we using MATRIX Scale Type
763
+                setImageViewScaleTypeMatrix(imageView);
764
+
765
+                // Update the base matrix using the current drawable
766
+                updateBaseMatrix(imageView.getDrawable());
767
+            } else {
768
+                // Reset the Matrix...
769
+                resetMatrix();
770
+            }
771
+        }
772
+    }
773
+
774
+    /**
775
+     * Get the display matrix
776
+     * @param matrix target matrix to copy to
777
+     */
778
+    @Override
779
+    public void getDisplayMatrix(Matrix matrix) {
780
+        matrix.set(getDrawMatrix());
781
+    }
782
+
783
+    /**
784
+     * Get the current support matrix
785
+     */
786
+    public void getSuppMatrix(Matrix matrix) {
787
+        matrix.set(mSuppMatrix);
788
+    }
789
+
790
+    private Matrix getDrawMatrix() {
791
+        mDrawMatrix.set(mBaseMatrix);
792
+        mDrawMatrix.postConcat(mSuppMatrix);
793
+        return mDrawMatrix;
794
+    }
795
+
796
+    private void cancelFling() {
797
+        if (null != mCurrentFlingRunnable) {
798
+            mCurrentFlingRunnable.cancelFling();
799
+            mCurrentFlingRunnable = null;
800
+        }
801
+    }
802
+
803
+    public Matrix getImageMatrix() {
804
+        return mDrawMatrix;
805
+    }
806
+
807
+    /**
808
+     * Helper method that simply checks the Matrix, and then displays the result
809
+     */
810
+    private void checkAndDisplayMatrix() {
811
+        if (checkMatrixBounds()) {
812
+            setImageViewMatrix(getDrawMatrix());
813
+        }
814
+    }
815
+
816
+    private void checkImageViewScaleType() {
817
+        ImageView imageView = getImageView();
818
+
819
+        /**
820
+         * PhotoView's getScaleType() will just divert to this.getScaleType() so
821
+         * only call if we're not attached to a PhotoView.
822
+         */
823
+        if (null != imageView && !(imageView instanceof IPhotoView)) {
824
+            if (!ScaleType.MATRIX.equals(imageView.getScaleType())) {
825
+                throw new IllegalStateException(
826
+                        "The ImageView's ScaleType has been changed since attaching a PhotoViewAttacher. You should call setScaleType on the PhotoViewAttacher instead of on the ImageView"  );
827
+            }
828
+        }
829
+    }
830
+
831
+    private boolean checkMatrixBounds() {
832
+        final ImageView imageView = getImageView();
833
+        if (null == imageView) {
834
+            return false;
835
+        }
836
+
837
+        final RectF rect = getDisplayRect(getDrawMatrix());
838
+        if (null == rect) {
839
+            return false;
840
+        }
841
+
842
+        final float height = rect.height(), width = rect.width();
843
+        float deltaX = 0, deltaY = 0;
844
+
845
+        final int viewHeight = getImageViewHeight(imageView);
846
+        if (height <= viewHeight) {
847
+            switch (mScaleType) {
848
+                case FIT_START:
849
+                    deltaY = -rect.top;
850
+                    break;
851
+                case FIT_END:
852
+                    deltaY = viewHeight - height - rect.top;
853
+                    break;
854
+                default:
855
+                    deltaY = (viewHeight - height) / 2 - rect.top;
856
+                    break;
857
+            }
858
+        } else if (rect.top > 0) {
859
+            deltaY = -rect.top;
860
+        } else if (rect.bottom < viewHeight) {
861
+            deltaY = viewHeight - rect.bottom;
862
+        }
863
+
864
+        final int viewWidth = getImageViewWidth(imageView);
865
+        if (width <= viewWidth) {
866
+            switch (mScaleType) {
867
+                case FIT_START:
868
+                    deltaX = -rect.left;
869
+                    break;
870
+                case FIT_END:
871
+                    deltaX = viewWidth - width - rect.left;
872
+                    break;
873
+                default:
874
+                    deltaX = (viewWidth - width) / 2 - rect.left;
875
+                    break;
876
+            }
877
+            mScrollEdge = EDGE_BOTH;
878
+        } else if (rect.left > 0) {
879
+            mScrollEdge = EDGE_LEFT;
880
+            deltaX = -rect.left;
881
+        } else if (rect.right < viewWidth) {
882
+            deltaX = viewWidth - rect.right;
883
+            mScrollEdge = EDGE_RIGHT;
884
+        } else {
885
+            mScrollEdge = EDGE_NONE;
886
+        }
887
+
888
+        // Finally actually translate the matrix
889
+        mSuppMatrix.postTranslate(deltaX, deltaY);
890
+
891
+        return true;
892
+    }
893
+
894
+    /**
895
+     * Helper method that maps the supplied Matrix to the current Drawable
896
+     *
897
+     * @param matrix - Matrix to map Drawable against
898
+     * @return RectF - Displayed Rectangle
899
+     */
900
+    private RectF getDisplayRect(Matrix matrix) {
901
+        ImageView imageView = getImageView();
902
+
903
+        if (null != imageView) {
904
+            Drawable d = imageView.getDrawable();
905
+            if (null != d) {
906
+                mDisplayRect.set(0, 0, d.getIntrinsicWidth(),
907
+                        d.getIntrinsicHeight());
908
+                matrix.mapRect(mDisplayRect);
909
+                return mDisplayRect;
910
+            }
911
+        }
912
+        return null;
913
+    }
914
+
915
+    public Bitmap getVisibleRectangleBitmap() {
916
+        ImageView imageView = getImageView();
917
+        return imageView == null ? null : imageView.getDrawingCache();
918
+    }
919
+
920
+    @Override
921
+    public void setZoomTransitionDuration(int milliseconds) {
922
+        if (milliseconds < 0)
923
+            milliseconds = DEFAULT_ZOOM_DURATION;
924
+        this.ZOOM_DURATION = milliseconds;
925
+    }
926
+
927
+    @Override
928
+    public IPhotoView getIPhotoViewImplementation() {
929
+        return this;
930
+    }
931
+
932
+    /**
933
+     * Helper method that 'unpacks' a Matrix and returns the required value
934
+     *
935
+     * @param matrix     - Matrix to unpack
936
+     * @param whichValue - Which value from Matrix.M* to return
937
+     * @return float - returned value
938
+     */
939
+    private float getValue(Matrix matrix, int whichValue) {
940
+        matrix.getValues(mMatrixValues);
941
+        return mMatrixValues[whichValue];
942
+    }
943
+
944
+    /**
945
+     * Resets the Matrix back to FIT_CENTER, and then displays it.s
946
+     */
947
+    private void resetMatrix() {
948
+        mSuppMatrix.reset();
949
+        setRotationBy(mBaseRotation);
950
+        setImageViewMatrix(getDrawMatrix());
951
+        checkMatrixBounds();
952
+    }
953
+
954
+    private void setImageViewMatrix(Matrix matrix) {
955
+        ImageView imageView = getImageView();
956
+        if (null != imageView) {
957
+
958
+            checkImageViewScaleType();
959
+            imageView.setImageMatrix(matrix);
960
+
961
+            // Call MatrixChangedListener if needed
962
+            if (null != mMatrixChangeListener) {
963
+                RectF displayRect = getDisplayRect(matrix);
964
+                if (null != displayRect) {
965
+                    mMatrixChangeListener.onMatrixChanged(displayRect);
966
+                }
967
+            }
968
+        }
969
+    }
970
+
971
+    /**
972
+     * Calculate Matrix for FIT_CENTER
973
+     *
974
+     * @param d - Drawable being displayed
975
+     */
976
+    private void updateBaseMatrix(Drawable d) {
977
+        ImageView imageView = getImageView();
978
+        if (null == imageView || null == d) {
979
+            return;
980
+        }
981
+
982
+        final float viewWidth = getImageViewWidth(imageView);
983
+        final float viewHeight = getImageViewHeight(imageView);
984
+        final int drawableWidth = d.getIntrinsicWidth();
985
+        final int drawableHeight = d.getIntrinsicHeight();
986
+
987
+        mBaseMatrix.reset();
988
+
989
+        final float widthScale = viewWidth / drawableWidth;
990
+        final float heightScale = viewHeight / drawableHeight;
991
+
992
+        if (mScaleType == ScaleType.CENTER) {
993
+            mBaseMatrix.postTranslate((viewWidth - drawableWidth) / 2F,
994
+                    (viewHeight - drawableHeight) / 2F);
995
+
996
+        } else if (mScaleType == ScaleType.CENTER_CROP) {
997
+            float scale = Math.max(widthScale, heightScale);
998
+            mBaseMatrix.postScale(scale, scale);
999
+            mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
1000
+                    (viewHeight - drawableHeight * scale) / 2F);
1001
+
1002
+        } else if (mScaleType == ScaleType.CENTER_INSIDE) {
1003
+            float scale = Math.min(1.0f, Math.min(widthScale, heightScale));
1004
+            mBaseMatrix.postScale(scale, scale);
1005
+            mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
1006
+                    (viewHeight - drawableHeight * scale) / 2F);
1007
+
1008
+        } else {
1009
+            RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight);
1010
+            RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight);
1011
+
1012
+            if ((int) mBaseRotation % 180 != 0) {
1013
+                mTempSrc = new RectF(0, 0, drawableHeight, drawableWidth);
1014
+            }
1015
+
1016
+            switch (mScaleType) {
1017
+                case FIT_CENTER:
1018
+                    mBaseMatrix
1019
+                            .setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER);
1020
+                    break;
1021
+
1022
+                case FIT_START:
1023
+                    mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.START);
1024
+                    break;
1025
+
1026
+                case FIT_END:
1027
+                    mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END);
1028
+                    break;
1029
+
1030
+                case FIT_XY:
1031
+                    mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.FILL);
1032
+                    break;
1033
+
1034
+                default:
1035
+                    break;
1036
+            }
1037
+        }
1038
+
1039
+        resetMatrix();
1040
+    }
1041
+
1042
+    private int getImageViewWidth(ImageView imageView) {
1043
+        if (null == imageView)
1044
+            return 0;
1045
+        return imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight();
1046
+    }
1047
+
1048
+    private int getImageViewHeight(ImageView imageView) {
1049
+        if (null == imageView)
1050
+            return 0;
1051
+        return imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom();
1052
+    }
1053
+
1054
+    /**
1055
+     * Interface definition for a callback to be invoked when the internal Matrix has changed for
1056
+     * this View.
1057
+     *
1058
+     * @author Chris Banes
1059
+     */
1060
+    public interface OnMatrixChangedListener {
1061
+        /**
1062
+         * Callback for when the Matrix displaying the Drawable has changed. This could be because
1063
+         * the View's bounds have changed, or the user has zoomed.
1064
+         *
1065
+         * @param rect - Rectangle displaying the Drawable's new bounds.
1066
+         */
1067
+        void onMatrixChanged(RectF rect);
1068
+    }
1069
+
1070
+    /**
1071
+     * Interface definition for callback to be invoked when attached ImageView scale changes
1072
+     *
1073
+     * @author Marek Sebera
1074
+     */
1075
+    public interface OnScaleChangeListener {
1076
+        /**
1077
+         * Callback for when the scale changes
1078
+         *
1079
+         * @param scaleFactor the scale factor (less than 1 for zoom out, greater than 1 for zoom in)
1080
+         * @param focusX      focal point X position
1081
+         * @param focusY      focal point Y position
1082
+         */
1083
+        void onScaleChange(float scaleFactor, float focusX, float focusY);
1084
+    }
1085
+
1086
+    /**
1087
+     * Interface definition for a callback to be invoked when the Photo is tapped with a single
1088
+     * tap.
1089
+     *
1090
+     * @author Chris Banes
1091
+     */
1092
+    public interface OnPhotoTapListener {
1093
+
1094
+        /**
1095
+         * A callback to receive where the user taps on a photo. You will only receive a callback if
1096
+         * the user taps on the actual photo, tapping on 'whitespace' will be ignored.
1097
+         *
1098
+         * @param view - View the user tapped.
1099
+         * @param x    - where the user tapped from the of the Drawable, as percentage of the
1100
+         *             Drawable width.
1101
+         * @param y    - where the user tapped from the top of the Drawable, as percentage of the
1102
+         *             Drawable height.
1103
+         */
1104
+        void onPhotoTap(View view, float x, float y);
1105
+
1106
+        /**
1107
+         * A simple callback where out of photo happened;
1108
+         * */
1109
+        void onOutsidePhotoTap();
1110
+    }
1111
+
1112
+    /**
1113
+     * Interface definition for a callback to be invoked when the ImageView is tapped with a single
1114
+     * tap.
1115
+     *
1116
+     * @author Chris Banes
1117
+     */
1118
+    public interface OnViewTapListener {
1119
+
1120
+        /**
1121
+         * A callback to receive where the user taps on a ImageView. You will receive a callback if
1122
+         * the user taps anywhere on the view, tapping on 'whitespace' will not be ignored.
1123
+         *
1124
+         * @param view - View the user tapped.
1125
+         * @param x    - where the user tapped from the left of the View.
1126
+         * @param y    - where the user tapped from the top of the View.
1127
+         */
1128
+        void onViewTap(View view, float x, float y);
1129
+    }
1130
+
1131
+    /**
1132
+     * Interface definition for a callback to be invoked when the ImageView is roateted with two finger.
1133
+     *
1134
+     * @author ChenSL
1135
+     */
1136
+    public interface OnRotateListener {
1137
+        /**
1138
+         * A callBack to receive when the user rotate a ImageView.You will receive a callback
1139
+         * if the user rotate the ImageView
1140
+         *
1141
+         * @param degree rotate mOldDegree
1142
+         */
1143
+        void onRotate(int degree);
1144
+    }
1145
+
1146
+    /**
1147
+     * Interface definition for a callback to be invoked when the ImageView is fling with a single
1148
+     * touch
1149
+     *
1150
+     * @author tonyjs
1151
+     */
1152
+    public interface OnSingleFlingListener {
1153
+
1154
+        /**
1155
+         * A callback to receive where the user flings on a ImageView. You will receive a callback if
1156
+         * the user flings anywhere on the view.
1157
+         *
1158
+         * @param e1        - MotionEvent the user first touch.
1159
+         * @param e2        - MotionEvent the user last touch.
1160
+         * @param velocityX - distance of user's horizontal fling.
1161
+         * @param velocityY - distance of user's vertical fling.
1162
+         */
1163
+        boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
1164
+    }
1165
+
1166
+    private class AnimatedZoomRunnable implements Runnable {
1167
+
1168
+        private final float mFocalX, mFocalY;
1169
+        private final long mStartTime;
1170
+        private final float mZoomStart, mZoomEnd;
1171
+
1172
+        public AnimatedZoomRunnable(final float currentZoom, final float targetZoom,
1173
+                                    final float focalX, final float focalY) {
1174
+            mFocalX = focalX;
1175
+            mFocalY = focalY;
1176
+            mStartTime = System.currentTimeMillis();
1177
+            mZoomStart = currentZoom;
1178
+            mZoomEnd = targetZoom;
1179
+        }
1180
+
1181
+        @Override
1182
+        public void run() {
1183
+            ImageView imageView = getImageView();
1184
+            if (imageView == null) {
1185
+                return;
1186
+            }
1187
+
1188
+            float t = interpolate();
1189
+            float scale = mZoomStart + t * (mZoomEnd - mZoomStart);
1190
+            float deltaScale = scale / getScale();
1191
+
1192
+            onScale(deltaScale, mFocalX, mFocalY);
1193
+
1194
+            // We haven't hit our target scale yet, so post ourselves again
1195
+            if (t < 1f) {
1196
+                Compat.postOnAnimation(imageView, this);
1197
+            }
1198
+        }
1199
+
1200
+        private float interpolate() {
1201
+            float t = 1f * (System.currentTimeMillis() - mStartTime) / ZOOM_DURATION;
1202
+            t = Math.min(1f, t);
1203
+            t = mInterpolator.getInterpolation(t);
1204
+            return t;
1205
+        }
1206
+    }
1207
+
1208
+    private class FlingRunnable implements Runnable {
1209
+
1210
+        private final ScrollerProxy mScroller;
1211
+        private int mCurrentX, mCurrentY;
1212
+
1213
+        public FlingRunnable(Context context) {
1214
+            mScroller = ScrollerProxy.getScroller(context);
1215
+        }
1216
+
1217
+        public void cancelFling() {
1218
+            if (DEBUG) {
1219
+                LogManager.getLogger().d(LOG_TAG, "Cancel Fling");
1220
+            }
1221
+            mScroller.forceFinished(true);
1222
+        }
1223
+
1224
+        public void fling(int viewWidth, int viewHeight, int velocityX,
1225
+                          int velocityY) {
1226
+            final RectF rect = getDisplayRect();
1227
+            if (null == rect) {
1228
+                return;
1229
+            }
1230
+
1231
+            final int startX = Math.round(-rect.left);
1232
+            final int minX, maxX, minY, maxY;
1233
+
1234
+            if (viewWidth < rect.width()) {
1235
+                minX = 0;
1236
+                maxX = Math.round(rect.width() - viewWidth);
1237
+            } else {
1238
+                minX = maxX = startX;
1239
+            }
1240
+
1241
+            final int startY = Math.round(-rect.top);
1242
+            if (viewHeight < rect.height()) {
1243
+                minY = 0;
1244
+                maxY = Math.round(rect.height() - viewHeight);
1245
+            } else {
1246
+                minY = maxY = startY;
1247
+            }
1248
+
1249
+            mCurrentX = startX;
1250
+            mCurrentY = startY;
1251
+
1252
+            if (DEBUG) {
1253
+                LogManager.getLogger().d(
1254
+                        LOG_TAG,
1255
+                        "fling. StartX:" + startX + " StartY:" + startY
1256
+                                + " MaxX:" + maxX + " MaxY:" + maxY);
1257
+            }
1258
+
1259
+            // If we actually can move, fling the scroller
1260
+            if (startX != maxX || startY != maxY) {
1261
+                mScroller.fling(startX, startY, velocityX, velocityY, minX,
1262
+                        maxX, minY, maxY, 0, 0);
1263
+            }
1264
+        }
1265
+
1266
+        @Override
1267
+        public void run() {
1268
+            if (mScroller.isFinished()) {
1269
+                return; // remaining post that should not be handled
1270
+            }
1271
+
1272
+            ImageView imageView = getImageView();
1273
+            if (null != imageView && mScroller.computeScrollOffset()) {
1274
+
1275
+                final int newX = mScroller.getCurrX();
1276
+                final int newY = mScroller.getCurrY();
1277
+
1278
+                if (DEBUG) {
1279
+                    LogManager.getLogger().d(
1280
+                            LOG_TAG,
1281
+                            "fling run(). CurrentX:" + mCurrentX + " CurrentY:"
1282
+                                    + mCurrentY + " NewX:" + newX + " NewY:"
1283
+                                    + newY);
1284
+                }
1285
+
1286
+                mSuppMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY);
1287
+                setImageViewMatrix(getDrawMatrix());
1288
+
1289
+                mCurrentX = newX;
1290
+                mCurrentY = newY;
1291
+
1292
+                // Post On animation
1293
+                Compat.postOnAnimation(imageView, this);
1294
+            }
1295
+        }
1296
+    }
1297
+
1298
+    /**
1299
+     * a RightAngleRunnable that finger lift rotate to 0,90,180,270 degree
1300
+     */
1301
+    private class RightAngleRunnable implements Runnable {
1302
+        private static final int RECOVER_SPEED = 4;
1303
+        private int mOldDegree;
1304
+        private int mNeedToRotate;
1305
+        private int mRoPivotX;
1306
+        private int mRoPivotY;
1307
+
1308
+        RightAngleRunnable(int degree, int pivotX, int pivotY) {
1309
+            this.mOldDegree = degree;
1310
+            this.mNeedToRotate = calDegree(degree) - mOldDegree;
1311
+            this.mRoPivotX = pivotX;
1312
+            this.mRoPivotY = pivotY;
1313
+        }
1314
+
1315
+        /**
1316
+         * get right degree,when one finger lifts
1317
+         *
1318
+         * @param oldDegree current degree
1319
+         * @return 0, 90, 180, 270 according to oldDegree
1320
+         */
1321
+        private int calDegree(int oldDegree) {
1322
+            int result;
1323
+            float n = (float) oldDegree / 45;
1324
+            if (n >= 0 && n < 1) {
1325
+                result = 0;
1326
+            } else if (n >= 1 && n <= 2.5) {
1327
+                result = 90;
1328
+            } else if (n > 2.5 && n < 5.5) {
1329
+                result = 180;
1330
+            } else if (n >= 5.5 && n <= 7) {
1331
+                result = 270;
1332
+            } else {
1333
+                result = 360;
1334
+            }
1335
+            return result;
1336
+        }
1337
+
1338
+        @Override
1339
+        public void run() {
1340
+            if (mNeedToRotate == 0) {
1341
+                mIsToRighting = false;
1342
+                return;
1343
+            }
1344
+            ImageView imageView = getImageView();
1345
+            if (imageView == null) {
1346
+                mIsToRighting = false;
1347
+                return;
1348
+            }
1349
+            mIsToRighting = true;
1350
+            if (mNeedToRotate > 0) {
1351
+                //Clockwise rotation
1352
+                if (mNeedToRotate >= RECOVER_SPEED) {
1353
+                    mSuppMatrix.postRotate(RECOVER_SPEED, mRoPivotX, mRoPivotY);
1354
+                    mNeedToRotate -= RECOVER_SPEED;
1355
+                } else {
1356
+                    mSuppMatrix.postRotate(mNeedToRotate, mRoPivotX, mRoPivotY);
1357
+                    mNeedToRotate = 0;
1358
+                }
1359
+            } else if (mNeedToRotate < 0) {
1360
+                //Counterclockwise rotation
1361
+                if (mNeedToRotate <= -RECOVER_SPEED) {
1362
+                    mSuppMatrix.postRotate(-RECOVER_SPEED, mRoPivotX, mRoPivotY);
1363
+                    mNeedToRotate += RECOVER_SPEED;
1364
+                } else {
1365
+                    mSuppMatrix.postRotate(mNeedToRotate, mRoPivotX, mRoPivotY);
1366
+                    mNeedToRotate = 0;
1367
+                }
1368
+            }
1369
+            checkAndDisplayMatrix();
1370
+            Compat.postOnAnimation(imageView, this);
1371
+        }
1372
+    }
1373
+}

+ 149 - 0
views/src/main/java/com/android/views/rotatephotoview/gestures/CupcakeGestureDetector.java

@@ -0,0 +1,149 @@
1
+/*******************************************************************************
2
+ * Copyright 2011, 2012 Chris Banes.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *******************************************************************************/
16
+package com.android.views.rotatephotoview.gestures;
17
+
18
+import android.content.Context;
19
+import android.view.MotionEvent;
20
+import android.view.VelocityTracker;
21
+import android.view.ViewConfiguration;
22
+
23
+import com.android.views.rotatephotoview.log.LogManager;
24
+
25
+public class CupcakeGestureDetector implements GestureDetector {
26
+
27
+    protected OnGestureListener mListener;
28
+    private static final String LOG_TAG = "CupcakeGestureDetector";
29
+    float mLastTouchX;
30
+    float mLastTouchY;
31
+    final float mTouchSlop;
32
+    final float mMinimumVelocity;
33
+
34
+    @Override
35
+    public void setOnGestureListener(OnGestureListener listener) {
36
+        this.mListener = listener;
37
+    }
38
+
39
+    public CupcakeGestureDetector(Context context) {
40
+        final ViewConfiguration configuration = ViewConfiguration
41
+                .get(context);
42
+        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
43
+        mTouchSlop = configuration.getScaledTouchSlop();
44
+    }
45
+
46
+    private VelocityTracker mVelocityTracker;
47
+    private boolean mIsDragging;
48
+
49
+    float getActiveX(MotionEvent ev) {
50
+        return ev.getX();
51
+    }
52
+
53
+    float getActiveY(MotionEvent ev) {
54
+        return ev.getY();
55
+    }
56
+
57
+    @Override
58
+    public boolean isScaling() {
59
+        return false;
60
+    }
61
+
62
+    @Override
63
+    public boolean isDragging() {
64
+        return mIsDragging;
65
+    }
66
+
67
+    @Override
68
+    public boolean onTouchEvent(MotionEvent ev) {
69
+        switch (ev.getAction()) {
70
+            case MotionEvent.ACTION_DOWN: {
71
+                mVelocityTracker = VelocityTracker.obtain();
72
+                if (null != mVelocityTracker) {
73
+                    mVelocityTracker.addMovement(ev);
74
+                } else {
75
+                    LogManager.getLogger().i(LOG_TAG, "Velocity tracker is null");
76
+                }
77
+
78
+                mLastTouchX = getActiveX(ev);
79
+                mLastTouchY = getActiveY(ev);
80
+                mIsDragging = false;
81
+                break;
82
+            }
83
+
84
+            case MotionEvent.ACTION_MOVE: {
85
+                final float x = getActiveX(ev);
86
+                final float y = getActiveY(ev);
87
+                final float dx = x - mLastTouchX, dy = y - mLastTouchY;
88
+
89
+                if (!mIsDragging) {
90
+                    // Use Pythagoras to see if drag length is larger than
91
+                    // touch slop
92
+                    mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
93
+                }
94
+
95
+                if (mIsDragging) {
96
+                    mListener.onDrag(dx, dy);
97
+                    mLastTouchX = x;
98
+                    mLastTouchY = y;
99
+
100
+                    if (null != mVelocityTracker) {
101
+                        mVelocityTracker.addMovement(ev);
102
+                    }
103
+                }
104
+                break;
105
+            }
106
+
107
+            case MotionEvent.ACTION_CANCEL: {
108
+                // Recycle Velocity Tracker
109
+                if (null != mVelocityTracker) {
110
+                    mVelocityTracker.recycle();
111
+                    mVelocityTracker = null;
112
+                }
113
+                break;
114
+            }
115
+
116
+            case MotionEvent.ACTION_UP: {
117
+                if (mIsDragging) {
118
+                    if (null != mVelocityTracker) {
119
+                        mLastTouchX = getActiveX(ev);
120
+                        mLastTouchY = getActiveY(ev);
121
+
122
+                        // Compute velocity within the last 1000ms
123
+                        mVelocityTracker.addMovement(ev);
124
+                        mVelocityTracker.computeCurrentVelocity(1000);
125
+
126
+                        final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker
127
+                                .getYVelocity();
128
+
129
+                        // If the velocity is greater than minVelocity, call
130
+                        // listener
131
+                        if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) {
132
+                            mListener.onFling(mLastTouchX, mLastTouchY, -vX,
133
+                                    -vY);
134
+                        }
135
+                    }
136
+                }
137
+
138
+                // Recycle Velocity Tracker
139
+                if (null != mVelocityTracker) {
140
+                    mVelocityTracker.recycle();
141
+                    mVelocityTracker = null;
142
+                }
143
+                break;
144
+            }
145
+        }
146
+
147
+        return true;
148
+    }
149
+}

+ 92 - 0
views/src/main/java/com/android/views/rotatephotoview/gestures/EclairGestureDetector.java

@@ -0,0 +1,92 @@
1
+/**
2
+ * ****************************************************************************
3
+ * Copyright 2011, 2012 Chris Banes.
4
+ * <p>
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ * <p>
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ * <p>
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *******************************************************************************/
17
+package com.android.views.rotatephotoview.gestures;
18
+
19
+import android.annotation.TargetApi;
20
+import android.content.Context;
21
+import android.view.MotionEvent;
22
+
23
+import com.android.views.rotatephotoview.Compat;
24
+
25
+@TargetApi(5)
26
+public class EclairGestureDetector extends CupcakeGestureDetector {
27
+
28
+    private static final int INVALID_POINTER_ID = -1;
29
+    private int mActivePointerId = INVALID_POINTER_ID;
30
+    private int mActivePointerIndex = 0;
31
+
32
+    public EclairGestureDetector(Context context) {
33
+        super(context);
34
+    }
35
+
36
+    @Override
37
+    float getActiveX(MotionEvent ev) {
38
+        try {
39
+            return ev.getX(mActivePointerIndex);
40
+        } catch (Exception e) {
41
+            return ev.getX();
42
+        }
43
+    }
44
+
45
+    @Override
46
+    float getActiveY(MotionEvent ev) {
47
+        try {
48
+            return ev.getY(mActivePointerIndex);
49
+        } catch (Exception e) {
50
+            return ev.getY();
51
+        }
52
+    }
53
+
54
+    @Override
55
+    public boolean onTouchEvent(MotionEvent ev) {
56
+        final int action = ev.getAction();
57
+        switch (action & MotionEvent.ACTION_MASK) {
58
+            case MotionEvent.ACTION_DOWN:
59
+                mActivePointerId = ev.getPointerId(0);
60
+                break;
61
+            case MotionEvent.ACTION_CANCEL:
62
+            case MotionEvent.ACTION_UP:
63
+                mActivePointerId = INVALID_POINTER_ID;
64
+                break;
65
+            case MotionEvent.ACTION_POINTER_UP:
66
+                // Ignore deprecation, ACTION_POINTER_ID_MASK and
67
+                // ACTION_POINTER_ID_SHIFT has same value and are deprecated
68
+                // You can have either deprecation or lint target api warning
69
+                final int pointerIndex = Compat.getPointerIndex(ev.getAction());
70
+                final int pointerId = ev.getPointerId(pointerIndex);
71
+                if (pointerId == mActivePointerId) {
72
+                    // This was our active pointer going up. Choose a new
73
+                    // active pointer and adjust accordingly.
74
+                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
75
+                    mActivePointerId = ev.getPointerId(newPointerIndex);
76
+                    mLastTouchX = ev.getX(newPointerIndex);
77
+                    mLastTouchY = ev.getY(newPointerIndex);
78
+                }
79
+                break;
80
+        }
81
+
82
+        mActivePointerIndex = ev
83
+                .findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId
84
+                        : 0);
85
+        try {
86
+            return super.onTouchEvent(ev);
87
+        } catch (IllegalArgumentException e) {
88
+            // Fix for support lib bug, happening when onDestroy is
89
+            return true;
90
+        }
91
+    }
92
+}

+ 73 - 0
views/src/main/java/com/android/views/rotatephotoview/gestures/FroyoGestureDetector.java

@@ -0,0 +1,73 @@
1
+/*******************************************************************************
2
+ * Copyright 2011, 2012 Chris Banes.
3
+ * <p/>
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ * <p/>
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ * <p/>
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *******************************************************************************/
16
+package com.android.views.rotatephotoview.gestures;
17
+
18
+import android.annotation.TargetApi;
19
+import android.content.Context;
20
+import android.view.MotionEvent;
21
+import android.view.ScaleGestureDetector;
22
+
23
+@TargetApi(8)
24
+public class FroyoGestureDetector extends EclairGestureDetector {
25
+
26
+    protected final ScaleGestureDetector mDetector;
27
+
28
+    public FroyoGestureDetector(Context context) {
29
+        super(context);
30
+        ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {
31
+
32
+            @Override
33
+            public boolean onScale(ScaleGestureDetector detector) {
34
+                float scaleFactor = detector.getScaleFactor();
35
+
36
+                if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
37
+                    return false;
38
+
39
+                mListener.onScale(scaleFactor,
40
+                        detector.getFocusX(), detector.getFocusY());
41
+                return true;
42
+            }
43
+
44
+            @Override
45
+            public boolean onScaleBegin(ScaleGestureDetector detector) {
46
+                return true;
47
+            }
48
+
49
+            @Override
50
+            public void onScaleEnd(ScaleGestureDetector detector) {
51
+                // NO-OP
52
+            }
53
+        };
54
+        mDetector = new ScaleGestureDetector(context, mScaleListener);
55
+    }
56
+
57
+    @Override
58
+    public boolean isScaling() {
59
+        return mDetector.isInProgress();
60
+    }
61
+
62
+    @Override
63
+    public boolean onTouchEvent(MotionEvent ev) {
64
+        try {
65
+            mDetector.onTouchEvent(ev);
66
+            return super.onTouchEvent(ev);
67
+        } catch (IllegalArgumentException e) {
68
+            // Fix for support lib bug, happening when onDestroy is
69
+            return true;
70
+        }
71
+    }
72
+
73
+}

+ 30 - 0
views/src/main/java/com/android/views/rotatephotoview/gestures/GestureDetector.java

@@ -0,0 +1,30 @@
1
+/*******************************************************************************
2
+ * Copyright 2011, 2012 Chris Banes.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *******************************************************************************/
16
+package com.android.views.rotatephotoview.gestures;
17
+
18
+import android.view.MotionEvent;
19
+
20
+public interface GestureDetector {
21
+
22
+    boolean onTouchEvent(MotionEvent ev);
23
+
24
+    boolean isScaling();
25
+
26
+    boolean isDragging();
27
+
28
+    void setOnGestureListener(OnGestureListener listener);
29
+
30
+}

+ 24 - 0
views/src/main/java/com/android/views/rotatephotoview/gestures/IRotateDetector.java

@@ -0,0 +1,24 @@
1
+package com.android.views.rotatephotoview.gestures;
2
+
3
+import android.view.MotionEvent;
4
+
5
+/**
6
+ * Interface to detect rotation
7
+ * Created by ChenSL on 2015/9/16.
8
+ */
9
+public interface IRotateDetector {
10
+    /**
11
+     * handle rotation in onTouchEvent
12
+     *
13
+     * @param event The motion event.
14
+     * @return True if the event was handled, false otherwise.
15
+     */
16
+    boolean onTouchEvent(MotionEvent event);
17
+
18
+    /**
19
+     * is the Gesture Rotate
20
+     *
21
+     * @return true:rotating;false,otherwise
22
+     */
23
+    boolean isRotating();
24
+}

+ 20 - 0
views/src/main/java/com/android/views/rotatephotoview/gestures/IRotateListener.java

@@ -0,0 +1,20 @@
1
+package com.android.views.rotatephotoview.gestures;
2
+
3
+/**
4
+ * Interface for a callback for rotation
5
+ * Created by ChenSL on 2015/9/16.
6
+ */
7
+public interface IRotateListener {
8
+    /**
9
+     * callback for rotation
10
+     *
11
+     * @param degree degree of rotation
12
+     */
13
+    void rotate(int degree, int pivotX, int pivotY);
14
+
15
+    /**
16
+     * MotionEvent.ACTION_POINTER_UP happens when two finger minus to only one
17
+     * change the ImageView to 0,90,180,270
18
+     */
19
+    void upRotate(int pivotX, int pivotY);
20
+}

+ 27 - 0
views/src/main/java/com/android/views/rotatephotoview/gestures/OnGestureListener.java

@@ -0,0 +1,27 @@
1
+/*******************************************************************************
2
+ * Copyright 2011, 2012 Chris Banes.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *******************************************************************************/
16
+package com.android.views.rotatephotoview.gestures;
17
+
18
+public interface OnGestureListener {
19
+
20
+    void onDrag(float dx, float dy);
21
+
22
+    void onFling(float startX, float startY, float velocityX,
23
+                 float velocityY);
24
+
25
+    void onScale(float scaleFactor, float focusX, float focusY);
26
+
27
+}

+ 113 - 0
views/src/main/java/com/android/views/rotatephotoview/gestures/RotateGestureDetector.java

@@ -0,0 +1,113 @@
1
+package com.android.views.rotatephotoview.gestures;
2
+
3
+import android.view.MotionEvent;
4
+
5
+/**
6
+ * Handle ImageView rotate event with two fingers
7
+ * Created by ChenSL on 2015/9/16.
8
+ */
9
+public class RotateGestureDetector implements IRotateDetector {
10
+    private int mLastAngle = 0;
11
+    private IRotateListener mListener;
12
+    private boolean mIsRotate;
13
+
14
+    /**
15
+     * set rotation listener for callback
16
+     *
17
+     * @param listener a  rotation listener
18
+     */
19
+    public void setRotateListener(IRotateListener listener) {
20
+        this.mListener = listener;
21
+    }
22
+
23
+    @Override
24
+    public boolean onTouchEvent(MotionEvent event) {
25
+        return doRotate(event);
26
+    }
27
+
28
+    @Override
29
+    public boolean isRotating() {
30
+        return mIsRotate;
31
+    }
32
+
33
+    /**
34
+     * handle rotation
35
+     *
36
+     * @param ev Motion event
37
+     * @return always true.
38
+     */
39
+    private boolean doRotate(MotionEvent ev) {
40
+        if (ev.getPointerCount() != 2) {
41
+            return false;
42
+        }
43
+        //Calculate the angle between the two fingers
44
+        int pivotX = (int) (ev.getX(0) + ev.getX(1)) / 2;
45
+        int pivotY = (int) (ev.getY(0) + ev.getY(1)) / 2;
46
+        float deltaX = ev.getX(0) - ev.getX(1);
47
+        float deltaY = ev.getY(0) - ev.getY(1);
48
+        double radians = Math.atan(deltaY / deltaX);
49
+        //Convert to degrees
50
+        int degrees = (int) (radians * 180 / Math.PI);
51
+        /*
52
+         * Must use getActionMasked() for switching to pick up pointer events.
53
+         * These events have the pointer index encoded in them so the return
54
+         * from getAction() won't match the exact action constant.
55
+         */
56
+        switch (ev.getActionMasked()) {
57
+            case MotionEvent.ACTION_DOWN:
58
+                mLastAngle = degrees;
59
+                mIsRotate = false;
60
+                break;
61
+            case MotionEvent.ACTION_UP:
62
+                mIsRotate = false;
63
+                break;
64
+            case MotionEvent.ACTION_POINTER_DOWN:
65
+                mLastAngle = degrees;
66
+                mIsRotate = false;
67
+                break;
68
+            case MotionEvent.ACTION_CANCEL:
69
+            case MotionEvent.ACTION_POINTER_UP:
70
+                mIsRotate = false;
71
+                upRotate(pivotX, pivotY);
72
+                mLastAngle = degrees;
73
+                break;
74
+            case MotionEvent.ACTION_MOVE:
75
+                mIsRotate = true;
76
+                int degreesValue = degrees - mLastAngle;
77
+                if (degreesValue > 45) {
78
+                    //Going CCW across the boundary
79
+                    rotate(-5, pivotX, pivotY);
80
+                } else if (degreesValue < -45) {
81
+                    //Going CW across the boundary
82
+                    rotate(5, pivotX, pivotY);
83
+                } else {
84
+                    //Normal rotation, rotate the difference
85
+                    rotate(degreesValue, pivotX, pivotY);
86
+                }
87
+                //Save the current angle
88
+                mLastAngle = degrees;
89
+                break;
90
+        }
91
+        return true;
92
+    }
93
+
94
+    /**
95
+     * to invoke the callback
96
+     *
97
+     * @param degree degree to rotate
98
+     */
99
+    private void rotate(int degree, int pivotX, int pivotY) {
100
+        if (mListener != null) {
101
+            mListener.rotate(degree, pivotX, pivotY);
102
+        }
103
+    }
104
+
105
+    /**
106
+     * to invoke the finger up action
107
+     */
108
+    private void upRotate(int pivotX, int pivotY) {
109
+        if (mListener != null) {
110
+            mListener.upRotate(pivotX, pivotY);
111
+        }
112
+    }
113
+}

+ 42 - 0
views/src/main/java/com/android/views/rotatephotoview/gestures/VersionedGestureDetector.java

@@ -0,0 +1,42 @@
1
+package com.android.views.rotatephotoview.gestures;
2
+
3
+/*******************************************************************************
4
+ * Copyright 2011, 2012 Chris Banes.
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *******************************************************************************/
18
+
19
+import android.content.Context;
20
+import android.os.Build;
21
+
22
+public final class VersionedGestureDetector {
23
+
24
+    public static GestureDetector newInstance(Context context,
25
+                                              OnGestureListener listener) {
26
+        final int sdkVersion = Build.VERSION.SDK_INT;
27
+        GestureDetector detector;
28
+
29
+        if (sdkVersion < Build.VERSION_CODES.ECLAIR) {
30
+            detector = new CupcakeGestureDetector(context);
31
+        } else if (sdkVersion < Build.VERSION_CODES.FROYO) {
32
+            detector = new EclairGestureDetector(context);
33
+        } else {
34
+            detector = new FroyoGestureDetector(context);
35
+        }
36
+
37
+        detector.setOnGestureListener(listener);
38
+
39
+        return detector;
40
+    }
41
+
42
+}

+ 35 - 0
views/src/main/java/com/android/views/rotatephotoview/log/LogManager.java

@@ -0,0 +1,35 @@
1
+/*******************************************************************************
2
+ * Copyright 2011, 2012 Chris Banes.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *******************************************************************************/
16
+package com.android.views.rotatephotoview.log;
17
+
18
+import android.util.Log;
19
+
20
+/**
21
+ * class that holds the {@link Logger} for this library, defaults to {@link LoggerDefault} to send logs to android {@link Log}
22
+ */
23
+public final class LogManager {
24
+
25
+    private static Logger logger = new LoggerDefault();
26
+
27
+    public static void setLogger(Logger newLogger) {
28
+        logger = newLogger;
29
+    }
30
+
31
+    public static Logger getLogger() {
32
+        return logger;
33
+    }
34
+
35
+}

+ 116 - 0
views/src/main/java/com/android/views/rotatephotoview/log/Logger.java

@@ -0,0 +1,116 @@
1
+/*******************************************************************************
2
+ * Copyright 2011, 2012 Chris Banes.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *******************************************************************************/
16
+package com.android.views.rotatephotoview.log;
17
+
18
+/**
19
+ * interface for a logger class to replace the static calls to {@link android.util.Log}
20
+ */
21
+public interface Logger {
22
+    /**
23
+     * Send a {@link android.util.Log#VERBOSE} log message.
24
+     *
25
+     * @param tag Used to identify the source of a log message.  It usually identifies
26
+     *            the class or activity where the log call occurs.
27
+     * @param msg The message you would like logged.
28
+     */
29
+    int v(String tag, String msg);
30
+
31
+    /**
32
+     * Send a {@link android.util.Log#VERBOSE} log message and log the exception.
33
+     *
34
+     * @param tag Used to identify the source of a log message.  It usually identifies
35
+     *            the class or activity where the log call occurs.
36
+     * @param msg The message you would like logged.
37
+     * @param tr  An exception to log
38
+     */
39
+    int v(String tag, String msg, Throwable tr);
40
+
41
+    /**
42
+     * Send a {@link android.util.Log#DEBUG} log message.
43
+     *
44
+     * @param tag Used to identify the source of a log message.  It usually identifies
45
+     *            the class or activity where the log call occurs.
46
+     * @param msg The message you would like logged.
47
+     */
48
+    int d(String tag, String msg);
49
+
50
+    /**
51
+     * Send a {@link android.util.Log#DEBUG} log message and log the exception.
52
+     *
53
+     * @param tag Used to identify the source of a log message.  It usually identifies
54
+     *            the class or activity where the log call occurs.
55
+     * @param msg The message you would like logged.
56
+     * @param tr  An exception to log
57
+     */
58
+    int d(String tag, String msg, Throwable tr);
59
+
60
+    /**
61
+     * Send an {@link android.util.Log#INFO} log message.
62
+     *
63
+     * @param tag Used to identify the source of a log message.  It usually identifies
64
+     *            the class or activity where the log call occurs.
65
+     * @param msg The message you would like logged.
66
+     */
67
+    int i(String tag, String msg);
68
+
69
+    /**
70
+     * Send a {@link android.util.Log#INFO} log message and log the exception.
71
+     *
72
+     * @param tag Used to identify the source of a log message.  It usually identifies
73
+     *            the class or activity where the log call occurs.
74
+     * @param msg The message you would like logged.
75
+     * @param tr  An exception to log
76
+     */
77
+    int i(String tag, String msg, Throwable tr);
78
+
79
+    /**
80
+     * Send a {@link android.util.Log#WARN} log message.
81
+     *
82
+     * @param tag Used to identify the source of a log message.  It usually identifies
83
+     *            the class or activity where the log call occurs.
84
+     * @param msg The message you would like logged.
85
+     */
86
+    int w(String tag, String msg);
87
+
88
+    /**
89
+     * Send a {@link android.util.Log#WARN} log message and log the exception.
90
+     *
91
+     * @param tag Used to identify the source of a log message.  It usually identifies
92
+     *            the class or activity where the log call occurs.
93
+     * @param msg The message you would like logged.
94
+     * @param tr  An exception to log
95
+     */
96
+    int w(String tag, String msg, Throwable tr);
97
+
98
+    /**
99
+     * Send an {@link android.util.Log#ERROR} log message.
100
+     *
101
+     * @param tag Used to identify the source of a log message.  It usually identifies
102
+     *            the class or activity where the log call occurs.
103
+     * @param msg The message you would like logged.
104
+     */
105
+    int e(String tag, String msg);
106
+
107
+    /**
108
+     * Send a {@link android.util.Log#ERROR} log message and log the exception.
109
+     *
110
+     * @param tag Used to identify the source of a log message.  It usually identifies
111
+     *            the class or activity where the log call occurs.
112
+     * @param msg The message you would like logged.
113
+     * @param tr  An exception to log
114
+     */
115
+    int e(String tag, String msg, Throwable tr);
116
+}

+ 76 - 0
views/src/main/java/com/android/views/rotatephotoview/log/LoggerDefault.java

@@ -0,0 +1,76 @@
1
+/*******************************************************************************
2
+ * Copyright 2011, 2012 Chris Banes.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *******************************************************************************/
16
+package com.android.views.rotatephotoview.log;
17
+
18
+import android.util.Log;
19
+
20
+/**
21
+ * Helper class to redirect {@link LogManager#logger} to {@link Log}
22
+ */
23
+public class LoggerDefault implements Logger {
24
+
25
+    @Override
26
+    public int v(String tag, String msg) {
27
+        return Log.v(tag, msg);
28
+    }
29
+
30
+    @Override
31
+    public int v(String tag, String msg, Throwable tr) {
32
+        return Log.v(tag, msg, tr);
33
+    }
34
+
35
+    @Override
36
+    public int d(String tag, String msg) {
37
+        return Log.d(tag, msg);
38
+    }
39
+
40
+    @Override
41
+    public int d(String tag, String msg, Throwable tr) {
42
+        return Log.d(tag, msg, tr);
43
+    }
44
+
45
+    @Override
46
+    public int i(String tag, String msg) {
47
+        return Log.i(tag, msg);
48
+    }
49
+
50
+    @Override
51
+    public int i(String tag, String msg, Throwable tr) {
52
+        return Log.i(tag, msg, tr);
53
+    }
54
+
55
+    @Override
56
+    public int w(String tag, String msg) {
57
+        return Log.w(tag, msg);
58
+    }
59
+
60
+    @Override
61
+    public int w(String tag, String msg, Throwable tr) {
62
+        return Log.w(tag, msg, tr);
63
+    }
64
+
65
+    @Override
66
+    public int e(String tag, String msg) {
67
+        return Log.e(tag, msg);
68
+    }
69
+
70
+    @Override
71
+    public int e(String tag, String msg, Throwable tr) {
72
+        return Log.e(tag, msg, tr);
73
+    }
74
+
75
+
76
+}

+ 61 - 0
views/src/main/java/com/android/views/rotatephotoview/scrollerproxy/GingerScroller.java

@@ -0,0 +1,61 @@
1
+/*******************************************************************************
2
+ * Copyright 2011, 2012 Chris Banes.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *******************************************************************************/
16
+package com.android.views.rotatephotoview.scrollerproxy;
17
+
18
+import android.annotation.TargetApi;
19
+import android.content.Context;
20
+import android.widget.OverScroller;
21
+
22
+@TargetApi(9)
23
+public class GingerScroller extends ScrollerProxy {
24
+
25
+    protected final OverScroller mScroller;
26
+
27
+    public GingerScroller(Context context) {
28
+        mScroller = new OverScroller(context);
29
+    }
30
+
31
+    @Override
32
+    public boolean computeScrollOffset() {
33
+        return mScroller.computeScrollOffset();
34
+    }
35
+
36
+    @Override
37
+    public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY,
38
+                      int overX, int overY) {
39
+        mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, overX, overY);
40
+    }
41
+
42
+    @Override
43
+    public void forceFinished(boolean finished) {
44
+        mScroller.forceFinished(finished);
45
+    }
46
+
47
+    @Override
48
+    public boolean isFinished() {
49
+        return mScroller.isFinished();
50
+    }
51
+
52
+    @Override
53
+    public int getCurrX() {
54
+        return mScroller.getCurrX();
55
+    }
56
+
57
+    @Override
58
+    public int getCurrY() {
59
+        return mScroller.getCurrY();
60
+    }
61
+}

+ 33 - 0
views/src/main/java/com/android/views/rotatephotoview/scrollerproxy/IcsScroller.java

@@ -0,0 +1,33 @@
1
+/*******************************************************************************
2
+ * Copyright 2011, 2012 Chris Banes.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *******************************************************************************/
16
+package com.android.views.rotatephotoview.scrollerproxy;
17
+
18
+import android.annotation.TargetApi;
19
+import android.content.Context;
20
+
21
+@TargetApi(14)
22
+public class IcsScroller extends GingerScroller {
23
+
24
+    public IcsScroller(Context context) {
25
+        super(context);
26
+    }
27
+
28
+    @Override
29
+    public boolean computeScrollOffset() {
30
+        return mScroller.computeScrollOffset();
31
+    }
32
+
33
+}

+ 58 - 0
views/src/main/java/com/android/views/rotatephotoview/scrollerproxy/PreGingerScroller.java

@@ -0,0 +1,58 @@
1
+/*******************************************************************************
2
+ * Copyright 2011, 2012 Chris Banes.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *******************************************************************************/
16
+package com.android.views.rotatephotoview.scrollerproxy;
17
+
18
+import android.content.Context;
19
+import android.widget.Scroller;
20
+
21
+public class PreGingerScroller extends ScrollerProxy {
22
+
23
+    private final Scroller mScroller;
24
+
25
+    public PreGingerScroller(Context context) {
26
+        mScroller = new Scroller(context);
27
+    }
28
+
29
+    @Override
30
+    public boolean computeScrollOffset() {
31
+        return mScroller.computeScrollOffset();
32
+    }
33
+
34
+    @Override
35
+    public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY,
36
+                      int overX, int overY) {
37
+        mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
38
+    }
39
+
40
+    @Override
41
+    public void forceFinished(boolean finished) {
42
+        mScroller.forceFinished(finished);
43
+    }
44
+
45
+    public boolean isFinished() {
46
+        return mScroller.isFinished();
47
+    }
48
+
49
+    @Override
50
+    public int getCurrX() {
51
+        return mScroller.getCurrX();
52
+    }
53
+
54
+    @Override
55
+    public int getCurrY() {
56
+        return mScroller.getCurrY();
57
+    }
58
+}

+ 48 - 0
views/src/main/java/com/android/views/rotatephotoview/scrollerproxy/ScrollerProxy.java

@@ -0,0 +1,48 @@
1
+/*******************************************************************************
2
+ * Copyright 2011, 2012 Chris Banes.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *******************************************************************************/
16
+package com.android.views.rotatephotoview.scrollerproxy;
17
+
18
+import android.content.Context;
19
+import android.os.Build.VERSION;
20
+import android.os.Build.VERSION_CODES;
21
+
22
+public abstract class ScrollerProxy {
23
+
24
+    public static ScrollerProxy getScroller(Context context) {
25
+        if (VERSION.SDK_INT < VERSION_CODES.GINGERBREAD) {
26
+            return new PreGingerScroller(context);
27
+        } else if (VERSION.SDK_INT < VERSION_CODES.ICE_CREAM_SANDWICH) {
28
+            return new GingerScroller(context);
29
+        } else {
30
+            return new IcsScroller(context);
31
+        }
32
+    }
33
+
34
+    public abstract boolean computeScrollOffset();
35
+
36
+    public abstract void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY,
37
+                               int maxY, int overX, int overY);
38
+
39
+    public abstract void forceFinished(boolean finished);
40
+
41
+    public abstract boolean isFinished();
42
+
43
+    public abstract int getCurrX();
44
+
45
+    public abstract int getCurrY();
46
+
47
+
48
+}

kodo - Gogs: Go Git Service

Няма описание

0009_auto_20170814_2001.py 698B

    # -*- coding: utf-8 -*- # Generated by Django 1.11.3 on 2017-08-14 12:01 from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('message', '0008_auto_20170315_2243'), ] operations = [ migrations.AlterField( model_name='systemmessageinfo', name='src', field=models.IntegerField(choices=[(0, '\u62cd\u7231\u7528\u6237\u7aef'), (1, '\u62cd\u7231\u6444\u5f71\u5e08\u7aef'), (1, '\u62cd\u7231\u6444\u5f71\u5e08\u7aef2'), (2, '\u62cd\u7231\u5bfc\u6e38\u7aef')], db_index=True, default=0, help_text='\u6765\u6e90', verbose_name='src'), ), ]