diff --git a/.gradle/2.8/taskArtifacts/cache.properties.lock b/.gradle/2.8/taskArtifacts/cache.properties.lock
index 7d9072d..bb5a154 100644
Binary files a/.gradle/2.8/taskArtifacts/cache.properties.lock and b/.gradle/2.8/taskArtifacts/cache.properties.lock differ
diff --git a/.gradle/2.8/taskArtifacts/fileHashes.bin b/.gradle/2.8/taskArtifacts/fileHashes.bin
index bbc7beb..7b8dc50 100644
Binary files a/.gradle/2.8/taskArtifacts/fileHashes.bin and b/.gradle/2.8/taskArtifacts/fileHashes.bin differ
diff --git a/.gradle/2.8/taskArtifacts/fileSnapshots.bin b/.gradle/2.8/taskArtifacts/fileSnapshots.bin
index f0e9285..9c0655b 100644
Binary files a/.gradle/2.8/taskArtifacts/fileSnapshots.bin and b/.gradle/2.8/taskArtifacts/fileSnapshots.bin differ
diff --git a/.gradle/2.8/taskArtifacts/outputFileStates.bin b/.gradle/2.8/taskArtifacts/outputFileStates.bin
index 6d516a1..e7576eb 100644
Binary files a/.gradle/2.8/taskArtifacts/outputFileStates.bin and b/.gradle/2.8/taskArtifacts/outputFileStates.bin differ
diff --git a/.gradle/2.8/taskArtifacts/taskArtifacts.bin b/.gradle/2.8/taskArtifacts/taskArtifacts.bin
index c457f2e..56016ad 100644
Binary files a/.gradle/2.8/taskArtifacts/taskArtifacts.bin and b/.gradle/2.8/taskArtifacts/taskArtifacts.bin differ
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index e046e46..7a5d518 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -10,6 +10,7 @@
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
index a094de9..c748180 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -4,6 +4,7 @@
+
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index cf7a959..4813806 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -6,7 +6,6 @@
-
@@ -25,115 +24,47 @@
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
-
-
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -165,6 +96,10 @@
+
+
+
+
@@ -522,6 +457,10 @@
+
+
+
+
@@ -530,6 +469,10 @@
+
+
+
+
@@ -607,6 +550,11 @@
+
+
+
+
+
@@ -736,14 +684,6 @@
-
-
-
-
-
-
-
-
@@ -1112,6 +1052,10 @@
+
+
+
+
@@ -1120,6 +1064,10 @@
+
+
+
+
@@ -1202,6 +1150,11 @@
+
+
+
+
+
@@ -1323,6 +1276,537 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1713,6 +2197,11 @@
+
+
+
+
+
@@ -1868,7 +2357,7 @@
+
+
+
+
+
+
+
+
@@ -2276,10 +2852,17 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -2287,7 +2870,7 @@
-
+
@@ -2367,288 +2950,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -2691,7 +2992,7 @@
-
+
@@ -2711,9 +3012,15 @@
+
+
+
+
+
+
@@ -2928,17 +3235,17 @@
-
+
-
+
-
+
@@ -2952,8 +3259,8 @@
-
-
+
+
@@ -2971,11 +3278,11 @@
-
+
-
+
@@ -2991,7 +3298,7 @@
-
+
@@ -3010,199 +3317,25 @@
-
+
+
+ file://$PROJECT_DIR$/PdfView/src/main/java/net/sf/andpdf/utils/MathUtils.java
+ 39
+
+
+
+
+ file://$PROJECT_DIR$/gestureimageview/src/main/java/com/polites/android/GestureImageView.java
+ 387
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -3221,6 +3354,7 @@
+
@@ -3267,13 +3401,6 @@
-
-
-
-
-
-
-
@@ -3478,22 +3605,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -3598,27 +3709,10 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@@ -3633,8 +3727,8 @@
-
-
+
+
@@ -3645,41 +3739,147 @@
-
+
-
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Android-Pdf-Viewer-Library.iml b/Android-Pdf-Viewer-Library.iml
index e6b0607..a8f705a 100644
--- a/Android-Pdf-Viewer-Library.iml
+++ b/Android-Pdf-Viewer-Library.iml
@@ -8,7 +8,7 @@
-
+
diff --git a/PdfView/PdfView.iml b/PdfView/PdfView.iml
index e76329a..85ebbf7 100644
--- a/PdfView/PdfView.iml
+++ b/PdfView/PdfView.iml
@@ -80,5 +80,6 @@
+
\ No newline at end of file
diff --git a/PdfView/build.gradle b/PdfView/build.gradle
index f75faa9..fef3f0b 100644
--- a/PdfView/build.gradle
+++ b/PdfView/build.gradle
@@ -1,18 +1,21 @@
apply plugin: 'com.android.library'
-
android {
- compileSdkVersion 21
- buildToolsVersion "23.0.2"
+ compileSdkVersion 21
+ buildToolsVersion "23.0.2"
- defaultConfig {
- minSdkVersion 4
- targetSdkVersion 4
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ defaultConfig {
+ minSdkVersion 14
+ targetSdkVersion 4
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ }
}
- }
}
+
+dependencies {
+ compile project(':gestureimageview')
+}
\ No newline at end of file
diff --git a/PdfView/build/intermediates/bundles/release/AndroidManifest.xml b/PdfView/build/intermediates/bundles/release/AndroidManifest.xml
index ecd6cca..59c9e3a 100644
--- a/PdfView/build/intermediates/bundles/release/AndroidManifest.xml
+++ b/PdfView/build/intermediates/bundles/release/AndroidManifest.xml
@@ -5,7 +5,7 @@
android:versionName="1.0" >
diff --git a/PdfView/build/intermediates/bundles/release/aapt/AndroidManifest.xml b/PdfView/build/intermediates/bundles/release/aapt/AndroidManifest.xml
index ecd6cca..59c9e3a 100644
--- a/PdfView/build/intermediates/bundles/release/aapt/AndroidManifest.xml
+++ b/PdfView/build/intermediates/bundles/release/aapt/AndroidManifest.xml
@@ -5,7 +5,7 @@
android:versionName="1.0" >
diff --git a/PdfView/build/intermediates/bundles/release/classes.jar b/PdfView/build/intermediates/bundles/release/classes.jar
index 105662e..780e726 100644
Binary files a/PdfView/build/intermediates/bundles/release/classes.jar and b/PdfView/build/intermediates/bundles/release/classes.jar differ
diff --git a/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView$1.class b/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView$1.class
index a80b68c..8f10343 100644
Binary files a/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView$1.class and b/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView$1.class differ
diff --git a/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView$2.class b/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView$2.class
index 91b685a..4f49bbe 100644
Binary files a/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView$2.class and b/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView$2.class differ
diff --git a/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView$3.class b/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView$3.class
index 124fc60..24369f9 100644
Binary files a/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView$3.class and b/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView$3.class differ
diff --git a/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView.class b/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView.class
index 819954a..e3f30d6 100644
Binary files a/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView.class and b/PdfView/build/intermediates/classes/release/net/sf/andpdf/pdfviewer/gui/PdfView.class differ
diff --git a/PdfView/build/intermediates/classes/release/net/sf/andpdf/utils/MathUtils.class b/PdfView/build/intermediates/classes/release/net/sf/andpdf/utils/MathUtils.class
new file mode 100644
index 0000000..7c2611c
Binary files /dev/null and b/PdfView/build/intermediates/classes/release/net/sf/andpdf/utils/MathUtils.class differ
diff --git a/PdfView/build/intermediates/classes/release/net/sf/andpdf/utils/VectorF.class b/PdfView/build/intermediates/classes/release/net/sf/andpdf/utils/VectorF.class
new file mode 100644
index 0000000..ec490d0
Binary files /dev/null and b/PdfView/build/intermediates/classes/release/net/sf/andpdf/utils/VectorF.class differ
diff --git a/PdfView/build/intermediates/exploded-aar/Android-Pdf-Viewer-Library/gestureimageview/unspecified/AndroidManifest.xml b/PdfView/build/intermediates/exploded-aar/Android-Pdf-Viewer-Library/gestureimageview/unspecified/AndroidManifest.xml
new file mode 100644
index 0000000..a4e05e6
--- /dev/null
+++ b/PdfView/build/intermediates/exploded-aar/Android-Pdf-Viewer-Library/gestureimageview/unspecified/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PdfView/build/intermediates/exploded-aar/Android-Pdf-Viewer-Library/gestureimageview/unspecified/aapt/AndroidManifest.xml b/PdfView/build/intermediates/exploded-aar/Android-Pdf-Viewer-Library/gestureimageview/unspecified/aapt/AndroidManifest.xml
new file mode 100644
index 0000000..a4e05e6
--- /dev/null
+++ b/PdfView/build/intermediates/exploded-aar/Android-Pdf-Viewer-Library/gestureimageview/unspecified/aapt/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PdfView/build/intermediates/exploded-aar/Android-Pdf-Viewer-Library/gestureimageview/unspecified/jars/classes.jar b/PdfView/build/intermediates/exploded-aar/Android-Pdf-Viewer-Library/gestureimageview/unspecified/jars/classes.jar
new file mode 100644
index 0000000..8662373
Binary files /dev/null and b/PdfView/build/intermediates/exploded-aar/Android-Pdf-Viewer-Library/gestureimageview/unspecified/jars/classes.jar differ
diff --git a/PdfView/build/intermediates/incremental/mergeResourcesrelease/merger.xml b/PdfView/build/intermediates/incremental/mergeResourcesrelease/merger.xml
new file mode 100644
index 0000000..4156877
--- /dev/null
+++ b/PdfView/build/intermediates/incremental/mergeResourcesrelease/merger.xml
@@ -0,0 +1,2 @@
+
+#9370DB#FFFFFFFF#000000#000000FFHello World, PdfViewerActivity!PdfViewer
\ No newline at end of file
diff --git a/PdfView/build/intermediates/res/merged/release/anim/slide_in.xml b/PdfView/build/intermediates/res/merged/release/anim/slide_in.xml
new file mode 100644
index 0000000..425217b
--- /dev/null
+++ b/PdfView/build/intermediates/res/merged/release/anim/slide_in.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PdfView/build/intermediates/res/merged/release/anim/slide_out.xml b/PdfView/build/intermediates/res/merged/release/anim/slide_out.xml
new file mode 100644
index 0000000..1569072
--- /dev/null
+++ b/PdfView/build/intermediates/res/merged/release/anim/slide_out.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PdfView/build/intermediates/res/merged/release/drawable-hdpi/icon.png b/PdfView/build/intermediates/res/merged/release/drawable-hdpi/icon.png
new file mode 100644
index 0000000..882eb14
Binary files /dev/null and b/PdfView/build/intermediates/res/merged/release/drawable-hdpi/icon.png differ
diff --git a/PdfView/build/intermediates/res/merged/release/drawable-ldpi/icon.png b/PdfView/build/intermediates/res/merged/release/drawable-ldpi/icon.png
new file mode 100644
index 0000000..18689f6
Binary files /dev/null and b/PdfView/build/intermediates/res/merged/release/drawable-ldpi/icon.png differ
diff --git a/PdfView/build/intermediates/res/merged/release/drawable-mdpi/icon.png b/PdfView/build/intermediates/res/merged/release/drawable-mdpi/icon.png
new file mode 100644
index 0000000..02e96b9
Binary files /dev/null and b/PdfView/build/intermediates/res/merged/release/drawable-mdpi/icon.png differ
diff --git a/PdfView/build/intermediates/res/merged/release/drawable/back01.png b/PdfView/build/intermediates/res/merged/release/drawable/back01.png
new file mode 100644
index 0000000..cfc029b
Binary files /dev/null and b/PdfView/build/intermediates/res/merged/release/drawable/back01.png differ
diff --git a/PdfView/build/intermediates/res/merged/release/drawable/back02.png b/PdfView/build/intermediates/res/merged/release/drawable/back02.png
new file mode 100644
index 0000000..7a3b8c1
Binary files /dev/null and b/PdfView/build/intermediates/res/merged/release/drawable/back02.png differ
diff --git a/PdfView/build/intermediates/res/merged/release/drawable/doc.png b/PdfView/build/intermediates/res/merged/release/drawable/doc.png
new file mode 100644
index 0000000..42d8213
Binary files /dev/null and b/PdfView/build/intermediates/res/merged/release/drawable/doc.png differ
diff --git a/PdfView/build/intermediates/res/merged/release/drawable/folder.png b/PdfView/build/intermediates/res/merged/release/drawable/folder.png
new file mode 100644
index 0000000..13b0189
Binary files /dev/null and b/PdfView/build/intermediates/res/merged/release/drawable/folder.png differ
diff --git a/PdfView/build/intermediates/res/merged/release/drawable/icon.png b/PdfView/build/intermediates/res/merged/release/drawable/icon.png
new file mode 100644
index 0000000..3285856
Binary files /dev/null and b/PdfView/build/intermediates/res/merged/release/drawable/icon.png differ
diff --git a/PdfView/build/intermediates/res/merged/release/drawable/left_arrow.png b/PdfView/build/intermediates/res/merged/release/drawable/left_arrow.png
new file mode 100644
index 0000000..fd14e22
Binary files /dev/null and b/PdfView/build/intermediates/res/merged/release/drawable/left_arrow.png differ
diff --git a/PdfView/build/intermediates/res/merged/release/drawable/pdf.png b/PdfView/build/intermediates/res/merged/release/drawable/pdf.png
new file mode 100644
index 0000000..1af99f7
Binary files /dev/null and b/PdfView/build/intermediates/res/merged/release/drawable/pdf.png differ
diff --git a/PdfView/build/intermediates/res/merged/release/drawable/right_arrow.png b/PdfView/build/intermediates/res/merged/release/drawable/right_arrow.png
new file mode 100644
index 0000000..e13abb8
Binary files /dev/null and b/PdfView/build/intermediates/res/merged/release/drawable/right_arrow.png differ
diff --git a/PdfView/build/intermediates/res/merged/release/drawable/zoom_in.png b/PdfView/build/intermediates/res/merged/release/drawable/zoom_in.png
new file mode 100644
index 0000000..2c80cd4
Binary files /dev/null and b/PdfView/build/intermediates/res/merged/release/drawable/zoom_in.png differ
diff --git a/PdfView/build/intermediates/res/merged/release/drawable/zoom_out.png b/PdfView/build/intermediates/res/merged/release/drawable/zoom_out.png
new file mode 100644
index 0000000..c26c3fd
Binary files /dev/null and b/PdfView/build/intermediates/res/merged/release/drawable/zoom_out.png differ
diff --git a/PdfView/build/intermediates/res/merged/release/layout/dialog_pagenumber.xml b/PdfView/build/intermediates/res/merged/release/layout/dialog_pagenumber.xml
new file mode 100644
index 0000000..4f51eb5
--- /dev/null
+++ b/PdfView/build/intermediates/res/merged/release/layout/dialog_pagenumber.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PdfView/build/intermediates/res/merged/release/layout/graphics_view.xml b/PdfView/build/intermediates/res/merged/release/layout/graphics_view.xml
new file mode 100644
index 0000000..f4c8804
--- /dev/null
+++ b/PdfView/build/intermediates/res/merged/release/layout/graphics_view.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PdfView/build/intermediates/res/merged/release/layout/main.xml b/PdfView/build/intermediates/res/merged/release/layout/main.xml
new file mode 100644
index 0000000..774162d
--- /dev/null
+++ b/PdfView/build/intermediates/res/merged/release/layout/main.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/PdfView/build/intermediates/res/merged/release/layout/navigation_overlay.xml b/PdfView/build/intermediates/res/merged/release/layout/navigation_overlay.xml
new file mode 100644
index 0000000..74fe032
--- /dev/null
+++ b/PdfView/build/intermediates/res/merged/release/layout/navigation_overlay.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PdfView/build/intermediates/res/merged/release/layout/pdf_file_password.xml b/PdfView/build/intermediates/res/merged/release/layout/pdf_file_password.xml
new file mode 100644
index 0000000..8c8d09c
--- /dev/null
+++ b/PdfView/build/intermediates/res/merged/release/layout/pdf_file_password.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PdfView/build/intermediates/res/merged/release/layout/scroll_layout.xml b/PdfView/build/intermediates/res/merged/release/layout/scroll_layout.xml
new file mode 100644
index 0000000..f066192
--- /dev/null
+++ b/PdfView/build/intermediates/res/merged/release/layout/scroll_layout.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PdfView/build/intermediates/res/merged/release/values/values.xml b/PdfView/build/intermediates/res/merged/release/values/values.xml
new file mode 100644
index 0000000..be7cb96
--- /dev/null
+++ b/PdfView/build/intermediates/res/merged/release/values/values.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ #000000
+ #000000FF
+ #9370DB
+ #FFFFFFFF
+
+
+ PdfViewer
+ Hello World, PdfViewerActivity!
+
\ No newline at end of file
diff --git a/PdfView/build/outputs/aar/PdfView-release.aar b/PdfView/build/outputs/aar/PdfView-release.aar
index f47b23b..016f94c 100644
Binary files a/PdfView/build/outputs/aar/PdfView-release.aar and b/PdfView/build/outputs/aar/PdfView-release.aar differ
diff --git a/PdfView/src/main/java/net/sf/andpdf/pdfviewer/gui/PdfView.java b/PdfView/src/main/java/net/sf/andpdf/pdfviewer/gui/PdfView.java
index 6deff8a..faae42d 100644
--- a/PdfView/src/main/java/net/sf/andpdf/pdfviewer/gui/PdfView.java
+++ b/PdfView/src/main/java/net/sf/andpdf/pdfviewer/gui/PdfView.java
@@ -6,11 +6,14 @@ import android.graphics.Color;
import android.graphics.RectF;
import android.os.Handler;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.ViewGroup;
+import android.view.WindowManager;
import android.widget.ImageButton;
-import android.widget.ImageView;
import android.widget.Toast;
+import com.polites.android.GestureImageView;
import com.sun.pdfview.PDFFile;
import com.sun.pdfview.PDFImage;
import com.sun.pdfview.PDFPage;
@@ -35,7 +38,7 @@ public class PdfView extends FullScrollView {
private static final float ZOOM_INCREMENT = 1.5f;
private Bitmap mBi;
- private ImageView mImageView;
+ private GestureImageView mImageView;
private Handler uiHandler;
ImageButton bZoomOut;
ImageButton bZoomIn;
@@ -59,11 +62,12 @@ public class PdfView extends FullScrollView {
PDFPaint.s_doAntiAlias = true;
uiHandler = new Handler();
LayoutParams matchLp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
- mImageView = new ImageView(context);
+ mImageView = new GestureImageView(context);
+
setPageBitmap(null);
updateImage();
addView(mImageView, matchLp);
- setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
setBackgroundColor(Color.LTGRAY);
setHorizontalScrollBarEnabled(true);
setHorizontalFadingEdgeEnabled(true);
@@ -79,6 +83,21 @@ public class PdfView extends FullScrollView {
this.mPdfFile = mPdfFile;
}
+ private int getDeviceWidth() {
+ DisplayMetrics metric = new DisplayMetrics();
+ WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+ wm.getDefaultDisplay().getMetrics(metric);
+ return metric.widthPixels; // 屏幕宽度(像素)
+
+ }
+
+ private int getDeviceHeight() {
+ DisplayMetrics metric = new DisplayMetrics();
+ WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+ wm.getDefaultDisplay().getMetrics(metric);
+ return metric.heightPixels; // 屏幕高度(像素)
+ }
+
public void showPage(int page, float zoom) throws Exception {
try {
// free memory from previous page
@@ -90,6 +109,13 @@ public class PdfView extends FullScrollView {
}
float width = mPdfPage.getWidth();
float height = mPdfPage.getHeight();
+ if (getLayoutParams().height == ViewGroup.LayoutParams.MATCH_PARENT) {
+ height *= getDeviceWidth() / width;
+ }
+ if (getLayoutParams().width == LayoutParams.MATCH_PARENT) {
+ width = getDeviceWidth();
+ }
+
RectF clip = null;
Bitmap bi = mPdfPage.getImage((int) (width * zoom), (int) (height * zoom), clip, true, true);
setPageBitmap(bi);
diff --git a/PdfView/src/main/java/net/sf/andpdf/utils/MathUtils.java b/PdfView/src/main/java/net/sf/andpdf/utils/MathUtils.java
new file mode 100755
index 0000000..9eb9e3c
--- /dev/null
+++ b/PdfView/src/main/java/net/sf/andpdf/utils/MathUtils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 net.sf.andpdf.utils;
+
+import android.graphics.PointF;
+import android.util.FloatMath;
+import android.view.MotionEvent;
+
+public class MathUtils {
+
+ public static float distance(MotionEvent event) {
+ float x = event.getX(0) - event.getX(1);
+ float y = event.getY(0) - event.getY(1);
+ return FloatMath.sqrt(x * x + y * y);
+ }
+
+ public static float distance(PointF p1, PointF p2) {
+ float x = p1.x - p2.x;
+ float y = p1.y - p2.y;
+ return FloatMath.sqrt(x * x + y * y);
+ }
+
+ public static float distance(float x1, float y1, float x2, float y2) {
+ float x = x1 - x2;
+ float y = y1 - y2;
+ return FloatMath.sqrt(x * x + y * y);
+ }
+
+ public static void midpoint(MotionEvent event, PointF point) {
+ float x1 = event.getX(0);
+ float y1 = event.getY(0);
+ float x2 = event.getX(1);
+ float y2 = event.getY(1);
+ midpoint(x1, y1, x2, y2, point);
+ }
+
+ public static void midpoint(float x1, float y1, float x2, float y2, PointF point) {
+ point.x = (x1 + x2) / 2.0f;
+ point.y = (y1 + y2) / 2.0f;
+ }
+
+ /**
+ * Rotates p1 around p2 by angle degrees.
+ *
+ * @param p1
+ * @param p2
+ * @param angle
+ */
+ public void rotate(PointF p1, PointF p2, float angle) {
+ float px = p1.x;
+ float py = p1.y;
+ float ox = p2.x;
+ float oy = p2.y;
+ p1.x = (FloatMath.cos(angle) * (px - ox) - FloatMath.sin(angle) * (py - oy) + ox);
+ p1.y = (FloatMath.sin(angle) * (px - ox) + FloatMath.cos(angle) * (py - oy) + oy);
+ }
+
+ public static float angle(PointF p1, PointF p2) {
+ return angle(p1.x, p1.y, p2.x, p2.y);
+ }
+
+ public static float angle(float x1, float y1, float x2, float y2) {
+ return (float) Math.atan2(y2 - y1, x2 - x1);
+ }
+}
diff --git a/PdfView/src/main/java/net/sf/andpdf/utils/VectorF.java b/PdfView/src/main/java/net/sf/andpdf/utils/VectorF.java
new file mode 100644
index 0000000..b0669af
--- /dev/null
+++ b/PdfView/src/main/java/net/sf/andpdf/utils/VectorF.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 net.sf.andpdf.utils;
+
+import android.graphics.PointF;
+import android.util.FloatMath;
+import android.view.MotionEvent;
+
+public class VectorF {
+
+ public float angle;
+ public float length;
+
+ public final PointF start = new PointF();
+ public final PointF end = new PointF();
+
+ public void calculateEndPoint() {
+ end.x = FloatMath.cos(angle) * length + start.x;
+ end.y = FloatMath.sin(angle) * length + start.y;
+ }
+
+ public void setStart(PointF p) {
+ this.start.x = p.x;
+ this.start.y = p.y;
+ }
+
+ public void setEnd(PointF p) {
+ this.end.x = p.x;
+ this.end.y = p.y;
+ }
+
+ public void set(MotionEvent event) {
+ this.start.x = event.getX(0);
+ this.start.y = event.getY(0);
+ this.end.x = event.getX(1);
+ this.end.y = event.getY(1);
+ }
+
+ public float calculateLength() {
+ length = MathUtils.distance(start, end);
+ return length;
+ }
+
+ public float calculateAngle() {
+ angle = MathUtils.angle(start, end);
+ return angle;
+ }
+
+
+}
diff --git a/build/intermediates/dex-cache/cache.xml b/build/intermediates/dex-cache/cache.xml
index b75495b..fc4d970 100644
--- a/build/intermediates/dex-cache/cache.xml
+++ b/build/intermediates/dex-cache/cache.xml
@@ -5,7 +5,7 @@
jar="/Users/winney/git/Android-Pdf-Viewer-Library/pdfviewsample/build/intermediates/exploded-aar/Android-Pdf-Viewer-Library/PdfView/unspecified/jars/classes.jar"
jumboMode="false"
revision="23.0.2"
- sha1="933be2f099767838f02cd4a28fc5ac4c219b327f">
+ sha1="e7d6dd07e1cac71ea7611e16c5dcd8b3cba978dc">
diff --git a/gestureimageview/build.gradle b/gestureimageview/build.gradle
new file mode 100644
index 0000000..34ad362
--- /dev/null
+++ b/gestureimageview/build.gradle
@@ -0,0 +1,18 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion "19.1.0"
+
+ defaultConfig {
+ minSdkVersion 7
+ targetSdkVersion 7
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ }
+ }
+}
diff --git a/gestureimageview/build/generated/source/buildConfig/release/com/polites/android/BuildConfig.java b/gestureimageview/build/generated/source/buildConfig/release/com/polites/android/BuildConfig.java
new file mode 100644
index 0000000..96b555d
--- /dev/null
+++ b/gestureimageview/build/generated/source/buildConfig/release/com/polites/android/BuildConfig.java
@@ -0,0 +1,13 @@
+/**
+ * Automatically generated file. DO NOT MODIFY
+ */
+package com.polites.android;
+
+public final class BuildConfig {
+ public static final boolean DEBUG = false;
+ public static final String APPLICATION_ID = "com.polites.android";
+ public static final String BUILD_TYPE = "release";
+ public static final String FLAVOR = "";
+ public static final int VERSION_CODE = 1;
+ public static final String VERSION_NAME = "";
+}
diff --git a/gestureimageview/build/intermediates/bundles/release/AndroidManifest.xml b/gestureimageview/build/intermediates/bundles/release/AndroidManifest.xml
new file mode 100644
index 0000000..a4e05e6
--- /dev/null
+++ b/gestureimageview/build/intermediates/bundles/release/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestureimageview/build/intermediates/bundles/release/aapt/AndroidManifest.xml b/gestureimageview/build/intermediates/bundles/release/aapt/AndroidManifest.xml
new file mode 100644
index 0000000..a4e05e6
--- /dev/null
+++ b/gestureimageview/build/intermediates/bundles/release/aapt/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestureimageview/build/intermediates/bundles/release/classes.jar b/gestureimageview/build/intermediates/bundles/release/classes.jar
new file mode 100644
index 0000000..8662373
Binary files /dev/null and b/gestureimageview/build/intermediates/bundles/release/classes.jar differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/Animation.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/Animation.class
new file mode 100644
index 0000000..d5aff61
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/Animation.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/Animator.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/Animator.class
new file mode 100644
index 0000000..4149aa3
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/Animator.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/BuildConfig.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/BuildConfig.class
new file mode 100644
index 0000000..bde5b11
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/BuildConfig.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/FlingAnimation.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/FlingAnimation.class
new file mode 100644
index 0000000..40b80fb
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/FlingAnimation.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/FlingAnimationListener.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/FlingAnimationListener.class
new file mode 100644
index 0000000..9460959
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/FlingAnimationListener.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/FlingListener.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/FlingListener.class
new file mode 100644
index 0000000..580f704
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/FlingListener.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageView$1.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageView$1.class
new file mode 100644
index 0000000..8b5775e
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageView$1.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageView$2.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageView$2.class
new file mode 100644
index 0000000..e28a367
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageView$2.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageView.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageView.class
new file mode 100644
index 0000000..de954bb
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageView.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewListener.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewListener.class
new file mode 100644
index 0000000..10009d8
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewListener.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener$1.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener$1.class
new file mode 100644
index 0000000..fbe7aa8
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener$1.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener$2.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener$2.class
new file mode 100644
index 0000000..c06e0f3
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener$2.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener$3.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener$3.class
new file mode 100644
index 0000000..c42b6cd
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener$3.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener$4.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener$4.class
new file mode 100644
index 0000000..68ec06f
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener$4.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener.class
new file mode 100644
index 0000000..4e69f0b
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/GestureImageViewTouchListener.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/MathUtils.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/MathUtils.class
new file mode 100644
index 0000000..e466223
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/MathUtils.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/MoveAnimation.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/MoveAnimation.class
new file mode 100644
index 0000000..ddabdfa
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/MoveAnimation.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/MoveAnimationListener.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/MoveAnimationListener.class
new file mode 100644
index 0000000..3ded8ad
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/MoveAnimationListener.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/VectorF.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/VectorF.class
new file mode 100644
index 0000000..d5ce5ba
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/VectorF.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/ZoomAnimation.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/ZoomAnimation.class
new file mode 100644
index 0000000..349c0d3
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/ZoomAnimation.class differ
diff --git a/gestureimageview/build/intermediates/classes/release/com/polites/android/ZoomAnimationListener.class b/gestureimageview/build/intermediates/classes/release/com/polites/android/ZoomAnimationListener.class
new file mode 100644
index 0000000..1857530
Binary files /dev/null and b/gestureimageview/build/intermediates/classes/release/com/polites/android/ZoomAnimationListener.class differ
diff --git a/gestureimageview/build/intermediates/incremental/aidl/release/dependency.store b/gestureimageview/build/intermediates/incremental/aidl/release/dependency.store
new file mode 100644
index 0000000..8b8400d
Binary files /dev/null and b/gestureimageview/build/intermediates/incremental/aidl/release/dependency.store differ
diff --git a/gestureimageview/build/intermediates/incremental/mergeAssets/release/merger.xml b/gestureimageview/build/intermediates/incremental/mergeAssets/release/merger.xml
new file mode 100644
index 0000000..6f36452
--- /dev/null
+++ b/gestureimageview/build/intermediates/incremental/mergeAssets/release/merger.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/gestureimageview/build/intermediates/incremental/packageResourcesrelease/merger.xml b/gestureimageview/build/intermediates/incremental/packageResourcesrelease/merger.xml
new file mode 100644
index 0000000..2a7baaa
--- /dev/null
+++ b/gestureimageview/build/intermediates/incremental/packageResourcesrelease/merger.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/gestureimageview/build/outputs/aar/gestureimageview-release.aar b/gestureimageview/build/outputs/aar/gestureimageview-release.aar
new file mode 100644
index 0000000..280affc
Binary files /dev/null and b/gestureimageview/build/outputs/aar/gestureimageview-release.aar differ
diff --git a/gestureimageview/build/tmp/packageReleaseJar/MANIFEST.MF b/gestureimageview/build/tmp/packageReleaseJar/MANIFEST.MF
new file mode 100644
index 0000000..58630c0
--- /dev/null
+++ b/gestureimageview/build/tmp/packageReleaseJar/MANIFEST.MF
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+
diff --git a/gestureimageview/gestureimageview.iml b/gestureimageview/gestureimageview.iml
new file mode 100644
index 0000000..079e01b
--- /dev/null
+++ b/gestureimageview/gestureimageview.iml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ generateDebugAndroidTestSources
+ generateDebugSources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestureimageview/src/main/AndroidManifest.xml b/gestureimageview/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ceae95b
--- /dev/null
+++ b/gestureimageview/src/main/AndroidManifest.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gestureimageview/src/main/java/com/polites/android/Animation.java b/gestureimageview/src/main/java/com/polites/android/Animation.java
new file mode 100644
index 0000000..6e3d3bc
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/Animation.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+/**
+ * @author Jason Polites
+ *
+ */
+public interface Animation {
+
+ /**
+ * Transforms the view.
+ * @param view
+ * @param diffTime
+ * @return true if this animation should remain active. False otherwise.
+ */
+ public boolean update(GestureImageView view, long time);
+
+}
diff --git a/gestureimageview/src/main/java/com/polites/android/Animator.java b/gestureimageview/src/main/java/com/polites/android/Animator.java
new file mode 100644
index 0000000..fbcb7d2
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/Animator.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public class Animator extends Thread {
+
+ private GestureImageView view;
+ private Animation animation;
+ private boolean running = false;
+ private boolean active = false;
+ private long lastTime = -1L;
+
+ public Animator(GestureImageView view, String threadName) {
+ super(threadName);
+ this.view = view;
+ }
+
+ @Override
+ public void run() {
+
+ running = true;
+
+ while(running) {
+
+ while(active && animation != null) {
+ long time = System.currentTimeMillis();
+ active = animation.update(view, time - lastTime);
+ view.redraw();
+ lastTime = time;
+
+ while(active) {
+ try {
+ if(view.waitForDraw(32)) { // 30Htz
+ break;
+ }
+ }
+ catch (InterruptedException ignore) {
+ active = false;
+ }
+ }
+ }
+
+ synchronized(this) {
+ if(running) {
+ try {
+ wait();
+ }
+ catch (InterruptedException ignore) {}
+ }
+ }
+ }
+ }
+
+ public synchronized void finish() {
+ running = false;
+ active = false;
+ notifyAll();
+ }
+
+ public void play(Animation transformer) {
+ if(active) {
+ cancel();
+ }
+ this.animation = transformer;
+
+ activate();
+ }
+
+ public synchronized void activate() {
+ lastTime = System.currentTimeMillis();
+ active = true;
+ notifyAll();
+ }
+
+ public void cancel() {
+ active = false;
+ }
+}
diff --git a/gestureimageview/src/main/java/com/polites/android/FlingAnimation.java b/gestureimageview/src/main/java/com/polites/android/FlingAnimation.java
new file mode 100644
index 0000000..66cdca5
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/FlingAnimation.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+/**
+ * @author Jason Polites
+ *
+ */
+public class FlingAnimation implements Animation {
+
+ private float velocityX;
+ private float velocityY;
+
+ private float factor = 0.95f;
+
+ private float threshold = 10;
+
+ private FlingAnimationListener listener;
+
+ /* (non-Javadoc)
+ * @see com.polites.android.Transformer#update(com.polites.android.GestureImageView, long)
+ */
+ @Override
+ public boolean update(GestureImageView view, long time) {
+ float seconds = (float) time / 1000.0f;
+
+ float dx = velocityX * seconds;
+ float dy = velocityY * seconds;
+
+ velocityX *= factor;
+ velocityY *= factor;
+
+ boolean active = (Math.abs(velocityX) > threshold && Math.abs(velocityY) > threshold);
+
+ if(listener != null) {
+ listener.onMove(dx, dy);
+
+ if(!active) {
+ listener.onComplete();
+ }
+ }
+
+ return active;
+ }
+
+ public void setVelocityX(float velocityX) {
+ this.velocityX = velocityX;
+ }
+
+ public void setVelocityY(float velocityY) {
+ this.velocityY = velocityY;
+ }
+
+ public void setFactor(float factor) {
+ this.factor = factor;
+ }
+
+ public void setListener(FlingAnimationListener listener) {
+ this.listener = listener;
+ }
+}
diff --git a/gestureimageview/src/main/java/com/polites/android/FlingAnimationListener.java b/gestureimageview/src/main/java/com/polites/android/FlingAnimationListener.java
new file mode 100644
index 0000000..b1b38e3
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/FlingAnimationListener.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public interface FlingAnimationListener {
+
+ public void onMove(float x, float y);
+
+ public void onComplete();
+
+}
diff --git a/gestureimageview/src/main/java/com/polites/android/FlingListener.java b/gestureimageview/src/main/java/com/polites/android/FlingListener.java
new file mode 100644
index 0000000..1b9b3dc
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/FlingListener.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.MotionEvent;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public class FlingListener extends SimpleOnGestureListener {
+
+ private float velocityX;
+ private float velocityY;
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+ this.velocityX = velocityX;
+ this.velocityY = velocityY;
+ return true;
+ }
+
+ public float getVelocityX() {
+ return velocityX;
+ }
+
+ public float getVelocityY() {
+ return velocityY;
+ }
+}
diff --git a/gestureimageview/src/main/java/com/polites/android/GestureImageView.java b/gestureimageview/src/main/java/com/polites/android/GestureImageView.java
new file mode 100644
index 0000000..05d4b81
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/GestureImageView.java
@@ -0,0 +1,713 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.ImageView;
+
+import java.io.InputStream;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class GestureImageView extends ImageView {
+
+ public static final String GLOBAL_NS = "http://schemas.android.com/apk/res/android";
+ public static final String LOCAL_NS = "http://schemas.polites.com/android";
+
+ private final Semaphore drawLock = new Semaphore(0);
+ private Animator animator;
+
+ private Drawable drawable;
+
+ private float x = 0, y = 0;
+
+ private boolean layout = false;
+
+ private float scaleAdjust = 1.0f;
+ private float startingScale = -1.0f;
+
+ private float scale = 1.0f;
+ private float maxScale = 5.0f;
+ private float minScale = 0.75f;
+ private float fitScaleHorizontal = 1.0f;
+ private float fitScaleVertical = 1.0f;
+ private float rotation = 0.0f;
+
+ private float centerX;
+ private float centerY;
+
+ private Float startX, startY;
+
+ private int hWidth;
+ private int hHeight;
+
+ private int resId = -1;
+ private boolean recycle = false;
+ private boolean strict = false;
+
+ private int displayHeight;
+ private int displayWidth;
+
+ private int alpha = 255;
+ private ColorFilter colorFilter;
+
+ private int deviceOrientation = -1;
+ private int imageOrientation;
+
+ private GestureImageViewListener gestureImageViewListener;
+ private GestureImageViewTouchListener gestureImageViewTouchListener;
+
+ private OnTouchListener customOnTouchListener;
+ private OnClickListener onClickListener;
+
+ public GestureImageView(Context context, AttributeSet attrs, int defStyle) {
+ this(context, attrs);
+ }
+
+ public GestureImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ String scaleType = attrs.getAttributeValue(GLOBAL_NS, "scaleType");
+
+ if (scaleType == null || scaleType.trim().length() == 0) {
+ setScaleType(ScaleType.CENTER_INSIDE);
+ }
+
+ String strStartX = attrs.getAttributeValue(LOCAL_NS, "start-x");
+ String strStartY = attrs.getAttributeValue(LOCAL_NS, "start-y");
+
+ if (strStartX != null && strStartX.trim().length() > 0) {
+ startX = Float.parseFloat(strStartX);
+ }
+
+ if (strStartY != null && strStartY.trim().length() > 0) {
+ startY = Float.parseFloat(strStartY);
+ }
+
+ setStartingScale(attrs.getAttributeFloatValue(LOCAL_NS, "start-scale", startingScale));
+ setMinScale(attrs.getAttributeFloatValue(LOCAL_NS, "min-scale", minScale));
+ setMaxScale(attrs.getAttributeFloatValue(LOCAL_NS, "max-scale", maxScale));
+ setStrict(attrs.getAttributeBooleanValue(LOCAL_NS, "strict", strict));
+ setRecycle(attrs.getAttributeBooleanValue(LOCAL_NS, "recycle", recycle));
+
+ initImage();
+ }
+
+ public GestureImageView(Context context) {
+ super(context);
+ setScaleType(ScaleType.CENTER_INSIDE);
+ initImage();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ if (drawable != null) {
+ int orientation = getResources().getConfiguration().orientation;
+ if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ displayHeight = MeasureSpec.getSize(heightMeasureSpec);
+
+ if (getLayoutParams().width == LayoutParams.WRAP_CONTENT) {
+ float ratio = (float) getImageWidth() / (float) getImageHeight();
+ displayWidth = Math.round((float) displayHeight * ratio);
+ } else {
+ displayWidth = MeasureSpec.getSize(widthMeasureSpec);
+ }
+ } else {
+ displayWidth = MeasureSpec.getSize(widthMeasureSpec);
+ if (getLayoutParams().height == LayoutParams.WRAP_CONTENT) {
+ float ratio = (float) getImageHeight() / (float) getImageWidth();
+ displayHeight = Math.round((float) displayWidth * ratio);
+ } else {
+ displayHeight = MeasureSpec.getSize(heightMeasureSpec);
+ }
+ }
+ } else {
+ displayHeight = MeasureSpec.getSize(heightMeasureSpec);
+ displayWidth = MeasureSpec.getSize(widthMeasureSpec);
+ }
+
+ setMeasuredDimension(displayWidth, displayHeight);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (changed || !layout) {
+ setupCanvas(displayWidth, displayHeight, getResources().getConfiguration().orientation);
+ }
+ }
+
+ protected void setupCanvas(int measuredWidth, int measuredHeight, int orientation) {
+
+ if (deviceOrientation != orientation) {
+ layout = false;
+ deviceOrientation = orientation;
+ }
+
+ if (drawable != null && !layout) {
+ int imageWidth = getImageWidth();
+ int imageHeight = getImageHeight();
+
+ hWidth = Math.round(((float) imageWidth / 2.0f));
+ hHeight = Math.round(((float) imageHeight / 2.0f));
+
+ measuredWidth -= (getPaddingLeft() + getPaddingRight());
+ measuredHeight -= (getPaddingTop() + getPaddingBottom());
+
+ computeCropScale(imageWidth, imageHeight, measuredWidth, measuredHeight);
+
+ if (startingScale <= 0.0f) {
+ computeStartingScale(imageWidth, imageHeight, measuredWidth, measuredHeight);
+ }
+
+ scaleAdjust = startingScale;
+
+ this.centerX = (float) measuredWidth / 2.0f;
+ this.centerY = (float) measuredHeight / 2.0f;
+
+ if (startX == null) {
+ x = centerX;
+ } else {
+ x = startX;
+ }
+
+ if (startY == null) {
+ y = centerY;
+ } else {
+ y = startY;
+ }
+
+ gestureImageViewTouchListener = new GestureImageViewTouchListener(this, measuredWidth, measuredHeight);
+
+ if (isLandscape()) {
+ gestureImageViewTouchListener.setMinScale(minScale * fitScaleHorizontal);
+ } else {
+ gestureImageViewTouchListener.setMinScale(minScale * fitScaleVertical);
+ }
+
+
+ gestureImageViewTouchListener.setMaxScale(maxScale * startingScale);
+
+ gestureImageViewTouchListener.setFitScaleHorizontal(fitScaleHorizontal);
+ gestureImageViewTouchListener.setFitScaleVertical(fitScaleVertical);
+ gestureImageViewTouchListener.setCanvasWidth(measuredWidth);
+ gestureImageViewTouchListener.setCanvasHeight(measuredHeight);
+ gestureImageViewTouchListener.setOnClickListener(onClickListener);
+
+ drawable.setBounds(-hWidth, -hHeight, hWidth, hHeight);
+
+ super.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (customOnTouchListener != null) {
+ customOnTouchListener.onTouch(v, event);
+ }
+ return gestureImageViewTouchListener.onTouch(v, event);
+ }
+ });
+
+ layout = true;
+ }
+ }
+
+ protected void computeCropScale(int imageWidth, int imageHeight, int measuredWidth, int measuredHeight) {
+ fitScaleHorizontal = (float) measuredWidth / (float) imageWidth;
+ fitScaleVertical = (float) measuredHeight / (float) imageHeight;
+ }
+
+ protected void computeStartingScale(int imageWidth, int imageHeight, int measuredWidth, int measuredHeight) {
+ switch (getScaleType()) {
+ case CENTER:
+ // Center the image in the view, but perform no scaling.
+ startingScale = 1.0f;
+ break;
+
+ case CENTER_CROP:
+ // Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions
+ // (width and height) of the image will be equal to or larger than the corresponding dimension of the view (minus padding).
+ startingScale = Math.max((float) measuredHeight / (float) imageHeight, (float) measuredWidth / (float) imageWidth);
+ break;
+
+ case CENTER_INSIDE:
+
+ // Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions
+ // (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding).
+ float wRatio = (float) imageWidth / (float) measuredWidth;
+ float hRatio = (float) imageHeight / (float) measuredHeight;
+
+ if (wRatio > hRatio) {
+ startingScale = fitScaleHorizontal;
+ } else {
+ startingScale = fitScaleVertical;
+ }
+
+ break;
+ }
+ }
+
+ protected boolean isRecycled() {
+ if (drawable != null && drawable instanceof BitmapDrawable) {
+ Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+ if (bitmap != null) {
+ return bitmap.isRecycled();
+ }
+ }
+ return false;
+ }
+
+ protected void recycle() {
+ if (recycle && drawable != null && drawable instanceof BitmapDrawable) {
+ Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
+ if (bitmap != null) {
+ bitmap.recycle();
+ }
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (layout) {
+ if (drawable != null && !isRecycled()) {
+ canvas.save();
+
+ float adjustedScale = scale * scaleAdjust;
+
+ canvas.translate(x, y);
+
+ if (rotation != 0.0f) {
+ canvas.rotate(rotation);
+ }
+
+ if (adjustedScale != 1.0f) {
+ canvas.scale(adjustedScale, adjustedScale);
+ }
+
+ drawable.draw(canvas);
+
+ canvas.restore();
+ }
+
+ if (drawLock.availablePermits() <= 0) {
+ drawLock.release();
+ }
+ }
+ }
+
+ /**
+ * Waits for a draw
+ *
+ * @param max time to wait for draw (ms)
+ * @throws InterruptedException
+ */
+ public boolean waitForDraw(long timeout) throws InterruptedException {
+ return drawLock.tryAcquire(timeout, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ animator = new Animator(this, "GestureImageViewAnimator");
+ animator.start();
+
+ if (resId >= 0 && drawable == null) {
+ setImageResource(resId);
+ }
+
+ super.onAttachedToWindow();
+ }
+
+ public void animationStart(Animation animation) {
+ if (animator != null) {
+ animator.play(animation);
+ }
+ }
+
+ public void animationStop() {
+ if (animator != null) {
+ animator.cancel();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ if (animator != null) {
+ animator.finish();
+ }
+ if (recycle && drawable != null && !isRecycled()) {
+ recycle();
+ drawable = null;
+ }
+ super.onDetachedFromWindow();
+ }
+
+ protected void initImage() {
+ if (this.drawable != null) {
+ this.drawable.setAlpha(alpha);
+ this.drawable.setFilterBitmap(true);
+ if (colorFilter != null) {
+ this.drawable.setColorFilter(colorFilter);
+ }
+ }
+
+ if (!layout) {
+ requestLayout();
+ redraw();
+ }
+ }
+
+ public void setImageBitmap(Bitmap image) {
+ this.drawable = new BitmapDrawable(getResources(), image);
+ initImage();
+ }
+
+ @Override
+ public void setImageDrawable(Drawable drawable) {
+ this.drawable = drawable;
+ initImage();
+ }
+
+ public void setImageResource(int id) {
+ if (this.drawable != null) {
+ this.recycle();
+ }
+ if (id >= 0) {
+ this.resId = id;
+ setImageDrawable(getContext().getResources().getDrawable(id));
+ }
+ }
+
+ public int getScaledWidth() {
+ return Math.round(getImageWidth() * getScale());
+ }
+
+ public int getScaledHeight() {
+ return Math.round(getImageHeight() * getScale());
+ }
+
+ public int getImageWidth() {
+ if (drawable != null) {
+ return drawable.getIntrinsicWidth();
+ }
+ return 0;
+ }
+
+ public int getImageHeight() {
+ if (drawable != null) {
+ return drawable.getIntrinsicHeight();
+ }
+ return 0;
+ }
+
+ public void moveBy(float x, float y) {
+ this.x += x;
+ this.y += y;
+ }
+
+ public void setPosition(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public void redraw() {
+ postInvalidate();
+ }
+
+ public void setMinScale(float min) {
+ this.minScale = min;
+ if (gestureImageViewTouchListener != null) {
+ gestureImageViewTouchListener.setMinScale(min * fitScaleHorizontal);
+ }
+ }
+
+ public void setMaxScale(float max) {
+ this.maxScale = max;
+ if (gestureImageViewTouchListener != null) {
+ gestureImageViewTouchListener.setMaxScale(max * startingScale);
+ }
+ }
+
+ public void setScale(float scale) {
+ scaleAdjust = scale;
+ }
+
+ public float getScale() {
+ return scaleAdjust;
+ }
+
+ public float getImageX() {
+ return x;
+ }
+
+ public float getImageY() {
+ return y;
+ }
+
+ public boolean isStrict() {
+ return strict;
+ }
+
+ public void setStrict(boolean strict) {
+ this.strict = strict;
+ }
+
+ public boolean isRecycle() {
+ return recycle;
+ }
+
+ public void setRecycle(boolean recycle) {
+ this.recycle = recycle;
+ }
+
+ public void reset() {
+ x = centerX;
+ y = centerY;
+ scaleAdjust = startingScale;
+ if (gestureImageViewTouchListener != null) {
+ gestureImageViewTouchListener.reset();
+ }
+ redraw();
+ }
+
+ public void setRotation(float rotation) {
+ this.rotation = rotation;
+ }
+
+ public void setGestureImageViewListener(GestureImageViewListener pinchImageViewListener) {
+ this.gestureImageViewListener = pinchImageViewListener;
+ }
+
+ public GestureImageViewListener getGestureImageViewListener() {
+ return gestureImageViewListener;
+ }
+
+ @Override
+ public Drawable getDrawable() {
+ return drawable;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ this.alpha = alpha;
+ if (drawable != null) {
+ drawable.setAlpha(alpha);
+ }
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ this.colorFilter = cf;
+ if (drawable != null) {
+ drawable.setColorFilter(cf);
+ }
+ }
+
+ @Override
+ public void setImageURI(Uri mUri) {
+ if ("content".equals(mUri.getScheme())) {
+ try {
+ String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
+
+ Cursor cur = getContext().getContentResolver().query(mUri, orientationColumn, null, null, null);
+
+ if (cur != null && cur.moveToFirst()) {
+ imageOrientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
+ }
+
+ InputStream in = null;
+
+ try {
+ in = getContext().getContentResolver().openInputStream(mUri);
+ Bitmap bmp = BitmapFactory.decodeStream(in);
+
+ if (imageOrientation != 0) {
+ Matrix m = new Matrix();
+ m.postRotate(imageOrientation);
+ Bitmap rotated = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
+ bmp.recycle();
+ setImageDrawable(new BitmapDrawable(getResources(), rotated));
+ } else {
+ setImageDrawable(new BitmapDrawable(getResources(), bmp));
+ }
+ } finally {
+ if (in != null) {
+ in.close();
+ }
+
+ if (cur != null) {
+ cur.close();
+ }
+ }
+ } catch (Exception e) {
+ Log.w("GestureImageView", "Unable to open content: " + mUri, e);
+ }
+ } else {
+ setImageDrawable(Drawable.createFromPath(mUri.toString()));
+ }
+
+ if (drawable == null) {
+ Log.e("GestureImageView", "resolveUri failed on bad bitmap uri: " + mUri);
+ // Don't try again.
+ mUri = null;
+ }
+ }
+
+ @Override
+ public Matrix getImageMatrix() {
+ if (strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ return super.getImageMatrix();
+ }
+
+ @Override
+ public void setScaleType(ScaleType scaleType) {
+ if (scaleType == ScaleType.CENTER ||
+ scaleType == ScaleType.CENTER_CROP ||
+ scaleType == ScaleType.CENTER_INSIDE) {
+
+ super.setScaleType(scaleType);
+ } else if (strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ }
+
+ @Override
+ public void invalidateDrawable(Drawable dr) {
+ if (strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ super.invalidateDrawable(dr);
+ }
+
+ @Override
+ public int[] onCreateDrawableState(int extraSpace) {
+ if (strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ return super.onCreateDrawableState(extraSpace);
+ }
+
+ @Override
+ public void setAdjustViewBounds(boolean adjustViewBounds) {
+ if (strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ super.setAdjustViewBounds(adjustViewBounds);
+ }
+
+ @Override
+ public void setImageLevel(int level) {
+ if (strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ super.setImageLevel(level);
+ }
+
+ @Override
+ public void setImageMatrix(Matrix matrix) {
+ if (strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ }
+
+ @Override
+ public void setImageState(int[] state, boolean merge) {
+ if (strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ }
+
+ @Override
+ public void setSelected(boolean selected) {
+ if (strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ super.setSelected(selected);
+ }
+
+ @Override
+ public void setOnTouchListener(OnTouchListener l) {
+ this.customOnTouchListener = l;
+ }
+
+ public float getCenterX() {
+ return centerX;
+ }
+
+ public float getCenterY() {
+ return centerY;
+ }
+
+ public boolean isLandscape() {
+ return getImageWidth() >= getImageHeight();
+ }
+
+ public boolean isPortrait() {
+ return getImageWidth() <= getImageHeight();
+ }
+
+ public void setStartingScale(float startingScale) {
+ this.startingScale = startingScale;
+ }
+
+ public void setStartingPosition(float x, float y) {
+ this.startX = x;
+ this.startY = y;
+ }
+
+ @Override
+ public void setOnClickListener(OnClickListener l) {
+ this.onClickListener = l;
+
+ if (gestureImageViewTouchListener != null) {
+ gestureImageViewTouchListener.setOnClickListener(l);
+ }
+ }
+
+ /**
+ * Returns true if the image dimensions are aligned with the orientation of the device.
+ *
+ * @return
+ */
+ public boolean isOrientationAligned() {
+ if (deviceOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ return isLandscape();
+ } else if (deviceOrientation == Configuration.ORIENTATION_PORTRAIT) {
+ return isPortrait();
+ }
+ return true;
+ }
+
+ public int getDeviceOrientation() {
+ return deviceOrientation;
+ }
+}
diff --git a/gestureimageview/src/main/java/com/polites/android/GestureImageViewListener.java b/gestureimageview/src/main/java/com/polites/android/GestureImageViewListener.java
new file mode 100644
index 0000000..ad5df10
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/GestureImageViewListener.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+/**
+ * @author jasonpolites
+ *
+ */
+public interface GestureImageViewListener {
+
+ public void onTouch(float x, float y);
+
+ public void onScale(float scale);
+
+ public void onPosition(float x, float y);
+
+}
diff --git a/gestureimageview/src/main/java/com/polites/android/GestureImageViewTouchListener.java b/gestureimageview/src/main/java/com/polites/android/GestureImageViewTouchListener.java
new file mode 100644
index 0000000..05945f5
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/GestureImageViewTouchListener.java
@@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+import android.content.res.Configuration;
+import android.graphics.PointF;
+import android.view.GestureDetector;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnTouchListener;
+
+public class GestureImageViewTouchListener implements OnTouchListener {
+
+ private GestureImageView image;
+ private OnClickListener onClickListener;
+
+ private final PointF current = new PointF();
+ private final PointF last = new PointF();
+ private final PointF next = new PointF();
+ private final PointF midpoint = new PointF();
+
+ private final VectorF scaleVector = new VectorF();
+ private final VectorF pinchVector = new VectorF();
+
+ private boolean touched = false;
+ private boolean inZoom = false;
+
+ private float initialDistance;
+ private float lastScale = 1.0f;
+ private float currentScale = 1.0f;
+
+ private float boundaryLeft = 0;
+ private float boundaryTop = 0;
+ private float boundaryRight = 0;
+ private float boundaryBottom = 0;
+
+ private float maxScale = 5.0f;
+ private float minScale = 0.25f;
+ private float fitScaleHorizontal = 1.0f;
+ private float fitScaleVertical = 1.0f;
+
+ private int canvasWidth = 0;
+ private int canvasHeight = 0;
+
+ private float centerX = 0;
+ private float centerY = 0;
+
+ private float startingScale = 0;
+
+ private boolean canDragX = false;
+ private boolean canDragY = false;
+
+ private boolean multiTouch = false;
+
+ private int displayWidth;
+ private int displayHeight;
+
+ private int imageWidth;
+ private int imageHeight;
+
+ private FlingListener flingListener;
+ private FlingAnimation flingAnimation;
+ private ZoomAnimation zoomAnimation;
+ private MoveAnimation moveAnimation;
+ private GestureDetector tapDetector;
+ private GestureDetector flingDetector;
+ private GestureImageViewListener imageListener;
+
+ public GestureImageViewTouchListener(final GestureImageView image, int displayWidth, int displayHeight) {
+ super();
+
+ this.image = image;
+
+ this.displayWidth = displayWidth;
+ this.displayHeight = displayHeight;
+
+ this.centerX = (float) displayWidth / 2.0f;
+ this.centerY = (float) displayHeight / 2.0f;
+
+ this.imageWidth = image.getImageWidth();
+ this.imageHeight = image.getImageHeight();
+
+ startingScale = image.getScale();
+
+ currentScale = startingScale;
+ lastScale = startingScale;
+
+ boundaryRight = displayWidth;
+ boundaryBottom = displayHeight;
+ boundaryLeft = 0;
+ boundaryTop = 0;
+
+ next.x = image.getImageX();
+ next.y = image.getImageY();
+
+ flingListener = new FlingListener();
+ flingAnimation = new FlingAnimation();
+ zoomAnimation = new ZoomAnimation();
+ moveAnimation = new MoveAnimation();
+
+ flingAnimation.setListener(new FlingAnimationListener() {
+ @Override
+ public void onMove(float x, float y) {
+ handleDrag(current.x + x, current.y + y);
+ }
+
+ @Override
+ public void onComplete() {}
+ });
+
+ zoomAnimation.setZoom(2.0f);
+ zoomAnimation.setZoomAnimationListener(new ZoomAnimationListener() {
+ @Override
+ public void onZoom(float scale, float x, float y) {
+ if(scale <= maxScale && scale >= minScale) {
+ handleScale(scale, x, y);
+ }
+ }
+
+ @Override
+ public void onComplete() {
+ inZoom = false;
+ handleUp();
+ }
+ });
+
+ moveAnimation.setMoveAnimationListener(new MoveAnimationListener() {
+
+ @Override
+ public void onMove(float x, float y) {
+ image.setPosition(x, y);
+ image.redraw();
+ }
+ });
+
+ tapDetector = new GestureDetector(image.getContext(), new SimpleOnGestureListener() {
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ startZoom(e);
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ if(!inZoom) {
+ if(onClickListener != null) {
+ onClickListener.onClick(image);
+ return true;
+ }
+ }
+
+ return false;
+ }
+ });
+
+ flingDetector = new GestureDetector(image.getContext(), flingListener);
+ imageListener = image.getGestureImageViewListener();
+
+ calculateBoundaries();
+ }
+
+ private void startFling() {
+ flingAnimation.setVelocityX(flingListener.getVelocityX());
+ flingAnimation.setVelocityY(flingListener.getVelocityY());
+ image.animationStart(flingAnimation);
+ }
+
+ private void startZoom(MotionEvent e) {
+ inZoom = true;
+ zoomAnimation.reset();
+
+ float zoomTo;
+
+ if(image.isLandscape()) {
+ if(image.getDeviceOrientation() == Configuration.ORIENTATION_PORTRAIT) {
+ int scaledHeight = image.getScaledHeight();
+
+ if(scaledHeight < canvasHeight) {
+ zoomTo = fitScaleVertical / currentScale;
+ zoomAnimation.setTouchX(e.getX());
+ zoomAnimation.setTouchY(image.getCenterY());
+ }
+ else {
+ zoomTo = fitScaleHorizontal / currentScale;
+ zoomAnimation.setTouchX(image.getCenterX());
+ zoomAnimation.setTouchY(image.getCenterY());
+ }
+ }
+ else {
+ int scaledWidth = image.getScaledWidth();
+
+ if(scaledWidth == canvasWidth) {
+ zoomTo = currentScale*4.0f;
+ zoomAnimation.setTouchX(e.getX());
+ zoomAnimation.setTouchY(e.getY());
+ }
+ else if(scaledWidth < canvasWidth) {
+ zoomTo = fitScaleHorizontal / currentScale;
+ zoomAnimation.setTouchX(image.getCenterX());
+ zoomAnimation.setTouchY(e.getY());
+ }
+ else {
+ zoomTo = fitScaleHorizontal / currentScale;
+ zoomAnimation.setTouchX(image.getCenterX());
+ zoomAnimation.setTouchY(image.getCenterY());
+ }
+ }
+ }
+ else {
+ if(image.getDeviceOrientation() == Configuration.ORIENTATION_PORTRAIT) {
+
+ int scaledHeight = image.getScaledHeight();
+
+ if(scaledHeight == canvasHeight) {
+ zoomTo = currentScale*4.0f;
+ zoomAnimation.setTouchX(e.getX());
+ zoomAnimation.setTouchY(e.getY());
+ }
+ else if(scaledHeight < canvasHeight) {
+ zoomTo = fitScaleVertical / currentScale;
+ zoomAnimation.setTouchX(e.getX());
+ zoomAnimation.setTouchY(image.getCenterY());
+ }
+ else {
+ zoomTo = fitScaleVertical / currentScale;
+ zoomAnimation.setTouchX(image.getCenterX());
+ zoomAnimation.setTouchY(image.getCenterY());
+ }
+ }
+ else {
+ int scaledWidth = image.getScaledWidth();
+
+ if(scaledWidth < canvasWidth) {
+ zoomTo = fitScaleHorizontal / currentScale;
+ zoomAnimation.setTouchX(image.getCenterX());
+ zoomAnimation.setTouchY(e.getY());
+ }
+ else {
+ zoomTo = fitScaleVertical / currentScale;
+ zoomAnimation.setTouchX(image.getCenterX());
+ zoomAnimation.setTouchY(image.getCenterY());
+ }
+ }
+ }
+
+ zoomAnimation.setZoom(zoomTo);
+ image.animationStart(zoomAnimation);
+ }
+
+
+ private void stopAnimations() {
+ image.animationStop();
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+
+ if(!inZoom) {
+
+ if(!tapDetector.onTouchEvent(event)) {
+ if(event.getPointerCount() == 1 && flingDetector.onTouchEvent(event)) {
+ startFling();
+ }
+
+ if(event.getAction() == MotionEvent.ACTION_UP) {
+ handleUp();
+ }
+ else if(event.getAction() == MotionEvent.ACTION_DOWN) {
+ stopAnimations();
+
+ last.x = event.getX();
+ last.y = event.getY();
+
+ if(imageListener != null) {
+ imageListener.onTouch(last.x, last.y);
+ }
+
+ touched = true;
+ }
+ else if(event.getAction() == MotionEvent.ACTION_MOVE) {
+ if(event.getPointerCount() > 1) {
+ multiTouch = true;
+ if(initialDistance > 0) {
+
+ pinchVector.set(event);
+ pinchVector.calculateLength();
+
+ float distance = pinchVector.length;
+
+ if(initialDistance != distance) {
+
+ float newScale = (distance / initialDistance) * lastScale;
+
+ if(newScale <= maxScale) {
+ scaleVector.length *= newScale;
+
+ scaleVector.calculateEndPoint();
+
+ scaleVector.length /= newScale;
+
+ float newX = scaleVector.end.x;
+ float newY = scaleVector.end.y;
+
+ handleScale(newScale, newX, newY);
+ }
+ }
+ }
+ else {
+ initialDistance = MathUtils.distance(event);
+
+ MathUtils.midpoint(event, midpoint);
+
+ scaleVector.setStart(midpoint);
+ scaleVector.setEnd(next);
+
+ scaleVector.calculateLength();
+ scaleVector.calculateAngle();
+
+ scaleVector.length /= lastScale;
+ }
+ }
+ else {
+ if(!touched) {
+ touched = true;
+ last.x = event.getX();
+ last.y = event.getY();
+ next.x = image.getImageX();
+ next.y = image.getImageY();
+ }
+ else if(!multiTouch) {
+ if(handleDrag(event.getX(), event.getY())) {
+ image.redraw();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ protected void handleUp() {
+
+ multiTouch = false;
+
+ initialDistance = 0;
+ lastScale = currentScale;
+
+ if(!canDragX) {
+ next.x = centerX;
+ }
+
+ if(!canDragY) {
+ next.y = centerY;
+ }
+
+ boundCoordinates();
+
+ if(!canDragX && !canDragY) {
+
+ if(image.isLandscape()) {
+ currentScale = fitScaleHorizontal;
+ lastScale = fitScaleHorizontal;
+ }
+ else {
+ currentScale = fitScaleVertical;
+ lastScale = fitScaleVertical;
+ }
+ }
+
+ image.setScale(currentScale);
+ image.setPosition(next.x, next.y);
+
+ if(imageListener != null) {
+ imageListener.onScale(currentScale);
+ imageListener.onPosition(next.x, next.y);
+ }
+
+ image.redraw();
+ }
+
+ protected void handleScale(float scale, float x, float y) {
+
+ currentScale = scale;
+
+ if(currentScale > maxScale) {
+ currentScale = maxScale;
+ }
+ else if (currentScale < minScale) {
+ currentScale = minScale;
+ }
+ else {
+ next.x = x;
+ next.y = y;
+ }
+
+ calculateBoundaries();
+
+ image.setScale(currentScale);
+ image.setPosition(next.x, next.y);
+
+ if(imageListener != null) {
+ imageListener.onScale(currentScale);
+ imageListener.onPosition(next.x, next.y);
+ }
+
+ image.redraw();
+ }
+
+ protected boolean handleDrag(float x, float y) {
+ current.x = x;
+ current.y = y;
+
+ float diffX = (current.x - last.x);
+ float diffY = (current.y - last.y);
+
+ if(diffX != 0 || diffY != 0) {
+
+ if(canDragX) next.x += diffX;
+ if(canDragY) next.y += diffY;
+
+ boundCoordinates();
+
+ last.x = current.x;
+ last.y = current.y;
+
+ if(canDragX || canDragY) {
+ image.setPosition(next.x, next.y);
+
+ if(imageListener != null) {
+ imageListener.onPosition(next.x, next.y);
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void reset() {
+ currentScale = startingScale;
+ next.x = centerX;
+ next.y = centerY;
+ calculateBoundaries();
+ image.setScale(currentScale);
+ image.setPosition(next.x, next.y);
+ image.redraw();
+ }
+
+
+ public float getMaxScale() {
+ return maxScale;
+ }
+
+ public void setMaxScale(float maxScale) {
+ this.maxScale = maxScale;
+ }
+
+ public float getMinScale() {
+ return minScale;
+ }
+
+ public void setMinScale(float minScale) {
+ this.minScale = minScale;
+ }
+
+ public void setOnClickListener(OnClickListener onClickListener) {
+ this.onClickListener = onClickListener;
+ }
+
+ protected void setCanvasWidth(int canvasWidth) {
+ this.canvasWidth = canvasWidth;
+ }
+
+ protected void setCanvasHeight(int canvasHeight) {
+ this.canvasHeight = canvasHeight;
+ }
+
+ protected void setFitScaleHorizontal(float fitScale) {
+ this.fitScaleHorizontal = fitScale;
+ }
+
+ protected void setFitScaleVertical(float fitScaleVertical) {
+ this.fitScaleVertical = fitScaleVertical;
+ }
+
+ protected void boundCoordinates() {
+ if(next.x < boundaryLeft) {
+ next.x = boundaryLeft;
+ }
+ else if(next.x > boundaryRight) {
+ next.x = boundaryRight;
+ }
+
+ if(next.y < boundaryTop) {
+ next.y = boundaryTop;
+ }
+ else if(next.y > boundaryBottom) {
+ next.y = boundaryBottom;
+ }
+ }
+
+ protected void calculateBoundaries() {
+
+ int effectiveWidth = Math.round( (float) imageWidth * currentScale );
+ int effectiveHeight = Math.round( (float) imageHeight * currentScale );
+
+ canDragX = effectiveWidth > displayWidth;
+ canDragY = effectiveHeight > displayHeight;
+
+ if(canDragX) {
+ float diff = (float)(effectiveWidth - displayWidth) / 2.0f;
+ boundaryLeft = centerX - diff;
+ boundaryRight = centerX + diff;
+ }
+
+ if(canDragY) {
+ float diff = (float)(effectiveHeight - displayHeight) / 2.0f;
+ boundaryTop = centerY - diff;
+ boundaryBottom = centerY + diff;
+ }
+ }
+}
diff --git a/gestureimageview/src/main/java/com/polites/android/MathUtils.java b/gestureimageview/src/main/java/com/polites/android/MathUtils.java
new file mode 100644
index 0000000..187f78e
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/MathUtils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+import android.graphics.PointF;
+import android.util.FloatMath;
+import android.view.MotionEvent;
+
+public class MathUtils {
+
+ public static float distance(MotionEvent event) {
+ float x = event.getX(0) - event.getX(1);
+ float y = event.getY(0) - event.getY(1);
+ return FloatMath.sqrt(x * x + y * y);
+ }
+
+ public static float distance(PointF p1, PointF p2) {
+ float x = p1.x - p2.x;
+ float y = p1.y - p2.y;
+ return FloatMath.sqrt(x * x + y * y);
+ }
+
+ public static float distance(float x1, float y1, float x2, float y2) {
+ float x = x1 - x2;
+ float y = y1 - y2;
+ return FloatMath.sqrt(x * x + y * y);
+ }
+
+ public static void midpoint(MotionEvent event, PointF point) {
+ float x1 = event.getX(0);
+ float y1 = event.getY(0);
+ float x2 = event.getX(1);
+ float y2 = event.getY(1);
+ midpoint(x1, y1, x2, y2, point);
+ }
+
+ public static void midpoint(float x1, float y1, float x2, float y2, PointF point) {
+ point.x = (x1 + x2) / 2.0f;
+ point.y = (y1 + y2) / 2.0f;
+ }
+
+ /**
+ * Rotates p1 around p2 by angle degrees.
+ *
+ * @param p1
+ * @param p2
+ * @param angle
+ */
+ public void rotate(PointF p1, PointF p2, float angle) {
+ float px = p1.x;
+ float py = p1.y;
+ float ox = p2.x;
+ float oy = p2.y;
+ p1.x = (FloatMath.cos(angle) * (px - ox) - FloatMath.sin(angle) * (py - oy) + ox);
+ p1.y = (FloatMath.sin(angle) * (px - ox) + FloatMath.cos(angle) * (py - oy) + oy);
+ }
+
+ public static float angle(PointF p1, PointF p2) {
+ return angle(p1.x, p1.y, p2.x, p2.y);
+ }
+
+ public static float angle(float x1, float y1, float x2, float y2) {
+ return (float) Math.atan2(y2 - y1, x2 - x1);
+ }
+}
diff --git a/gestureimageview/src/main/java/com/polites/android/MoveAnimation.java b/gestureimageview/src/main/java/com/polites/android/MoveAnimation.java
new file mode 100644
index 0000000..ccb8812
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/MoveAnimation.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public class MoveAnimation implements Animation {
+
+ private boolean firstFrame = true;
+
+ private float startX;
+ private float startY;
+
+ private float targetX;
+ private float targetY;
+ private long animationTimeMS = 100;
+ private long totalTime = 0;
+
+ private MoveAnimationListener moveAnimationListener;
+
+ /* (non-Javadoc)
+ * @see com.polites.android.Animation#update(com.polites.android.GestureImageView, long)
+ */
+ @Override
+ public boolean update(GestureImageView view, long time) {
+ totalTime += time;
+
+ if(firstFrame) {
+ firstFrame = false;
+ startX = view.getImageX();
+ startY = view.getImageY();
+ }
+
+ if(totalTime < animationTimeMS) {
+
+ float ratio = (float) totalTime / animationTimeMS;
+
+ float newX = ((targetX - startX) * ratio) + startX;
+ float newY = ((targetY - startY) * ratio) + startY;
+
+ if(moveAnimationListener != null) {
+ moveAnimationListener.onMove(newX, newY);
+ }
+
+ return true;
+ }
+ else {
+ if(moveAnimationListener != null) {
+ moveAnimationListener.onMove(targetX, targetY);
+ }
+ }
+
+ return false;
+ }
+
+ public void reset() {
+ firstFrame = true;
+ totalTime = 0;
+ }
+
+
+ public float getTargetX() {
+ return targetX;
+ }
+
+
+ public void setTargetX(float targetX) {
+ this.targetX = targetX;
+ }
+
+
+ public float getTargetY() {
+ return targetY;
+ }
+
+ public void setTargetY(float targetY) {
+ this.targetY = targetY;
+ }
+
+ public long getAnimationTimeMS() {
+ return animationTimeMS;
+ }
+
+ public void setAnimationTimeMS(long animationTimeMS) {
+ this.animationTimeMS = animationTimeMS;
+ }
+
+ public void setMoveAnimationListener(MoveAnimationListener moveAnimationListener) {
+ this.moveAnimationListener = moveAnimationListener;
+ }
+}
diff --git a/gestureimageview/src/main/java/com/polites/android/MoveAnimationListener.java b/gestureimageview/src/main/java/com/polites/android/MoveAnimationListener.java
new file mode 100644
index 0000000..cb570b2
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/MoveAnimationListener.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public interface MoveAnimationListener {
+
+ public void onMove(float x, float y);
+
+}
diff --git a/gestureimageview/src/main/java/com/polites/android/VectorF.java b/gestureimageview/src/main/java/com/polites/android/VectorF.java
new file mode 100644
index 0000000..ce23663
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/VectorF.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+import android.graphics.PointF;
+import android.util.FloatMath;
+import android.view.MotionEvent;
+
+public class VectorF {
+
+ public float angle;
+ public float length;
+
+ public final PointF start = new PointF();
+ public final PointF end = new PointF();
+
+ public void calculateEndPoint() {
+ end.x = FloatMath.cos(angle) * length + start.x;
+ end.y = FloatMath.sin(angle) * length + start.y;
+ }
+
+ public void setStart(PointF p) {
+ this.start.x = p.x;
+ this.start.y = p.y;
+ }
+
+ public void setEnd(PointF p) {
+ this.end.x = p.x;
+ this.end.y = p.y;
+ }
+
+ public void set(MotionEvent event) {
+ this.start.x = event.getX(0);
+ this.start.y = event.getY(0);
+ this.end.x = event.getX(1);
+ this.end.y = event.getY(1);
+ }
+
+ public float calculateLength() {
+ length = MathUtils.distance(start, end);
+ return length;
+ }
+
+ public float calculateAngle() {
+ angle = MathUtils.angle(start, end);
+ return angle;
+ }
+
+
+}
diff --git a/gestureimageview/src/main/java/com/polites/android/ZoomAnimation.java b/gestureimageview/src/main/java/com/polites/android/ZoomAnimation.java
new file mode 100644
index 0000000..5cceeb0
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/ZoomAnimation.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+import android.graphics.PointF;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public class ZoomAnimation implements Animation {
+
+ private boolean firstFrame = true;
+
+ private float touchX;
+ private float touchY;
+
+ private float zoom;
+
+ private float startX;
+ private float startY;
+ private float startScale;
+
+ private float xDiff;
+ private float yDiff;
+ private float scaleDiff;
+
+ private long animationLengthMS = 200;
+ private long totalTime = 0;
+
+ private ZoomAnimationListener zoomAnimationListener;
+
+ /* (non-Javadoc)
+ * @see com.polites.android.Animation#update(com.polites.android.GestureImageView, long)
+ */
+ @Override
+ public boolean update(GestureImageView view, long time) {
+ if(firstFrame) {
+ firstFrame = false;
+
+ startX = view.getImageX();
+ startY = view.getImageY();
+ startScale = view.getScale();
+ scaleDiff = (zoom * startScale) - startScale;
+
+ if(scaleDiff > 0) {
+ // Calculate destination for midpoint
+ VectorF vector = new VectorF();
+
+ // Set the touch point as start because we want to move the end
+ vector.setStart(new PointF(touchX, touchY));
+ vector.setEnd(new PointF(startX, startY));
+
+ vector.calculateAngle();
+
+ // Get the current length
+ float length = vector.calculateLength();
+
+ // Multiply length by zoom to get the new length
+ vector.length = length*zoom;
+
+ // Now deduce the new endpoint
+ vector.calculateEndPoint();
+
+ xDiff = vector.end.x - startX;
+ yDiff = vector.end.y - startY;
+ }
+ else {
+ // Zoom out to center
+ xDiff = view.getCenterX() - startX;
+ yDiff = view.getCenterY() - startY;
+ }
+ }
+
+ totalTime += time;
+
+ float ratio = (float) totalTime / (float) animationLengthMS;
+
+ if(ratio < 1) {
+
+ if(ratio > 0) {
+ // we still have time left
+ float newScale = (ratio * scaleDiff) + startScale;
+ float newX = (ratio * xDiff) + startX;
+ float newY = (ratio * yDiff) + startY;
+
+ if(zoomAnimationListener != null) {
+ zoomAnimationListener.onZoom(newScale, newX, newY);
+ }
+ }
+
+ return true;
+ }
+ else {
+
+ float newScale = scaleDiff + startScale;
+ float newX = xDiff + startX;
+ float newY = yDiff + startY;
+
+ if(zoomAnimationListener != null) {
+ zoomAnimationListener.onZoom(newScale, newX, newY);
+ zoomAnimationListener.onComplete();
+ }
+
+ return false;
+ }
+ }
+
+ public void reset() {
+ firstFrame = true;
+ totalTime = 0;
+ }
+
+ public float getZoom() {
+ return zoom;
+ }
+
+ public void setZoom(float zoom) {
+ this.zoom = zoom;
+ }
+
+ public float getTouchX() {
+ return touchX;
+ }
+
+ public void setTouchX(float touchX) {
+ this.touchX = touchX;
+ }
+
+ public float getTouchY() {
+ return touchY;
+ }
+
+ public void setTouchY(float touchY) {
+ this.touchY = touchY;
+ }
+
+ public long getAnimationLengthMS() {
+ return animationLengthMS;
+ }
+
+ public void setAnimationLengthMS(long animationLengthMS) {
+ this.animationLengthMS = animationLengthMS;
+ }
+
+ public ZoomAnimationListener getZoomAnimationListener() {
+ return zoomAnimationListener;
+ }
+
+ public void setZoomAnimationListener(ZoomAnimationListener zoomAnimationListener) {
+ this.zoomAnimationListener = zoomAnimationListener;
+ }
+}
diff --git a/gestureimageview/src/main/java/com/polites/android/ZoomAnimationListener.java b/gestureimageview/src/main/java/com/polites/android/ZoomAnimationListener.java
new file mode 100644
index 0000000..4d50898
--- /dev/null
+++ b/gestureimageview/src/main/java/com/polites/android/ZoomAnimationListener.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * 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 com.polites.android;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public interface ZoomAnimationListener {
+ public void onZoom(float scale, float x, float y);
+ public void onComplete();
+}
diff --git a/import-summary.txt b/import-summary.txt
index 36146c6..a66c6fd 100644
--- a/import-summary.txt
+++ b/import-summary.txt
@@ -7,22 +7,10 @@ The following files were *not* copied into the new Gradle project; you
should evaluate whether these are still needed in your project and if
so manually move them:
-* .idea/
-* .idea/.name
-* .idea/compiler.xml
-* .idea/copyright/
-* .idea/copyright/profiles_settings.xml
-* .idea/libraries/
-* .idea/libraries/PdfViewer.xml
-* .idea/misc.xml
-* .idea/modules.xml
-* .idea/vcs.xml
-* .idea/workspace.xml
-* Android-Pdf-Viewer-Library.iml
-* PdfViewer.jar
-* README.txt
+* .DS_Store
+* .gitignore
* build.xml
-* default.properties
+* gesture-imageview.iml
* proguard.cfg
Moved Files:
@@ -30,28 +18,11 @@ Moved Files:
Android Gradle projects use a different directory structure than ADT
Eclipse projects. Here's how the projects were restructured:
-* AndroidManifest.xml => app/src/main/AndroidManifest.xml
-* crypto-src/ => app/src/main/java/
-* res/ => app/src/main/res/
-* src/ => app/src/main/java/
-* src/com/sun/pdfview/.cvsignore => app/src/main/resources/com/sun/pdfview/.cvsignore
-* src/com/sun/pdfview/decode/CCITTCodes => app/src/main/resources/com/sun/pdfview/decode/CCITTCodes
-* src/com/sun/pdfview/font/res/BaseFonts.properties => app/src/main/resources/com/sun/pdfview/font/res/BaseFonts.properties
-* src/com/sun/pdfview/font/res/d050000l.pfb => app/src/main/resources/com/sun/pdfview/font/res/d050000l.pfb
-* src/com/sun/pdfview/font/res/n019003l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n019003l.pfb
-* src/com/sun/pdfview/font/res/n019004l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n019004l.pfb
-* src/com/sun/pdfview/font/res/n019023l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n019023l.pfb
-* src/com/sun/pdfview/font/res/n019024l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n019024l.pfb
-* src/com/sun/pdfview/font/res/n021003l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n021003l.pfb
-* src/com/sun/pdfview/font/res/n021004l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n021004l.pfb
-* src/com/sun/pdfview/font/res/n021023l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n021023l.pfb
-* src/com/sun/pdfview/font/res/n021024l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n021024l.pfb
-* src/com/sun/pdfview/font/res/n022003l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n022003l.pfb
-* src/com/sun/pdfview/font/res/n022004l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n022004l.pfb
-* src/com/sun/pdfview/font/res/n022023l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n022023l.pfb
-* src/com/sun/pdfview/font/res/n022024l.pfb => app/src/main/resources/com/sun/pdfview/font/res/n022024l.pfb
-* src/com/sun/pdfview/font/res/s050000l.pfb => app/src/main/resources/com/sun/pdfview/font/res/s050000l.pfb
-* src/com/sun/pdfview/font/ttf/resource/glyphlist.txt => app/src/main/resources/com/sun/pdfview/font/ttf/resource/glyphlist.txt
+* AndroidManifest.xml => gestureimageview/src/main/AndroidManifest.xml
+* src/ => gestureimageview/src/main/java/
+* src/.DS_Store => gestureimageview/src/main/resources/.DS_Store
+* src/com/.DS_Store => gestureimageview/src/main/resources/com/.DS_Store
+* src/com/polites/.DS_Store => gestureimageview/src/main/resources/com/polites/.DS_Store
Next Steps:
-----------
diff --git a/pdfviewsample/src/main/res/layout/activity_main2.xml b/pdfviewsample/src/main/res/layout/activity_main2.xml
index 6a3c4d7..fc0e60a 100644
--- a/pdfviewsample/src/main/res/layout/activity_main2.xml
+++ b/pdfviewsample/src/main/res/layout/activity_main2.xml
@@ -7,8 +7,8 @@
diff --git a/settings.gradle b/settings.gradle
index 0dbb0ca..8f90eb5 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1,2 @@
include ':PdfView', ':pdfviewsample'
+include ':gestureimageview'