Browse Source

android support libraries -> androidx
use photoview instead of imageviewtouch (included because of TOP_CROP missing)
remove obsolete/unused code

Andrew Dolgov 1 month ago
parent
commit
12f64363c8
42 changed files with 1649 additions and 1225 deletions
  1. 2 0
      gradle.properties
  2. 5 6
      org.fox.ttcomics/build.gradle
  3. 45 42
      org.fox.ttcomics/org.fox.ttcomics.iml
  4. 3 3
      org.fox.ttcomics/src/main/AndroidManifest.xml
  5. 39 0
      org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/Compat.java
  6. 205 0
      org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/CustomGestureDetector.java
  7. 27 0
      org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnGestureListener.java
  8. 18 0
      org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnMatrixChangedListener.java
  9. 14 0
      org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnOutsidePhotoTapListener.java
  10. 22 0
      org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnPhotoTapListener.java
  11. 17 0
      org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnScaleChangedListener.java
  12. 21 0
      org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnSingleFlingListener.java
  13. 16 0
      org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnViewDragListener.java
  14. 16 0
      org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnViewTapListener.java
  15. 256 0
      org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/PhotoView.java
  16. 822 0
      org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java
  17. 37 0
      org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/Util.java
  18. 0 268
      org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouch.java
  19. 0 512
      org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouchBase.java
  20. 0 20
      org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/easing/Cubic.java
  21. 0 10
      org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/easing/Easing.java
  22. 0 84
      org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/graphics/FastBitmapDrawable.java
  23. 0 14
      org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/graphics/IBitmapDrawable.java
  24. 0 6
      org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/utils/IDisposable.java
  25. 2 2
      org.fox.ttcomics/src/main/java/org/fox/ttcomics2/Application.java
  26. 35 145
      org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicFragment.java
  27. 2 4
      org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicListFragment.java
  28. 14 11
      org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicPager.java
  29. 4 3
      org.fox.ttcomics/src/main/java/org/fox/ttcomics2/CommonActivity.java
  30. 7 7
      org.fox.ttcomics/src/main/java/org/fox/ttcomics2/MainActivity.java
  31. 2 1
      org.fox.ttcomics/src/main/java/org/fox/ttcomics2/PreferencesActivity.java
  32. 2 1
      org.fox.ttcomics/src/main/java/org/fox/ttcomics2/StateSavedFragment.java
  33. 3 3
      org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ViewComicActivity.java
  34. 2 1
      org.fox.ttcomics/src/main/java/org/fox/ttcomics2/utils/CacheCleanupService.java
  35. 0 33
      org.fox.ttcomics/src/main/java/org/fox/ttcomics2/utils/CompatListActivity.java
  36. 0 39
      org.fox.ttcomics/src/main/java/org/fox/ttcomics2/utils/IVTViewPager.java
  37. 3 3
      org.fox.ttcomics/src/main/res/layout/activity_main.xml
  38. 2 2
      org.fox.ttcomics/src/main/res/layout/comics_grid_row.xml
  39. 2 1
      org.fox.ttcomics/src/main/res/layout/fragment_comic.xml
  40. 2 2
      org.fox.ttcomics/src/main/res/layout/fragment_comics_grid.xml
  41. 1 1
      org.fox.ttcomics/src/main/res/layout/fragment_comics_pager.xml
  42. 1 1
      org.fox.ttcomics/src/main/res/layout/toolbar.xml

+ 2 - 0
gradle.properties

@@ -0,0 +1,2 @@
+android.enableJetifier=true
+android.useAndroidX=true

+ 5 - 6
org.fox.ttcomics/build.gradle

@@ -47,10 +47,9 @@ android {
 }
 
 dependencies {
-    implementation 'com.android.support:support-v4:28.0.0'
-    implementation 'com.android.support:appcompat-v7:28.0.0'
-    implementation 'com.android.support:cardview-v7:28.0.0'
-    implementation 'com.android.support:design:28.0.0'
+    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+    implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'
+    implementation 'com.google.android.material:material:1.1.0-alpha03'
     implementation 'com.shamanland:fab:0.0.8'
     implementation 'jp.co.recruit_mp:android-HeaderFooterGridView:0.2.4'
     implementation 'com.github.bumptech.glide:glide:3.8.0'
@@ -61,8 +60,8 @@ dependencies {
     compileOnly 'frankiesardo:icepick-processor:3.2.0'
     implementation 'com.github.livefront:bridge:v1.1.2'
     annotationProcessor 'frankiesardo:icepick-processor:3.2.0'
-    implementation 'com.google.android.gms:play-services-base:15.0.1'
-    implementation 'com.google.android.gms:play-services-auth:15.0.1'
+    implementation 'com.google.android.gms:play-services-base:16.1.0'
+    implementation 'com.google.android.gms:play-services-auth:16.0.1'
     implementation files('libs/nineoldandroids-2.4.0.jar')
     implementation 'com.gu:option:1.3'
     implementation 'net.rdrei.android.dirchooser:library:[email protected]'

+ 45 - 42
org.fox.ttcomics/org.fox.ttcomics.iml

@@ -125,69 +125,72 @@
     </content>
     <orderEntry type="jdk" jdkName="Android API 28 Platform" jdkType="Android SDK" />
     <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntry type="library" name="Gradle: com.android.support:design:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: stencil:stencil:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-fragment:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:localbroadcastmanager:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: org.clojure:data.priority-map:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:animated-vector-drawable:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.lifecycle:viewmodel:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:loader:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.core:runtime:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata-core:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.drawerlayout:drawerlayout:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.documentfile:documentfile:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.localbroadcastmanager:localbroadcastmanager:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.cardview:cardview:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-runtime:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-livedata-core:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-base:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: net.rdrei.android.dirchooser:library:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:cursoradapter:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.cursoradapter:cursoradapter:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.core:core:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.customview:customview:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: __local_aars__:C.\Users\fox\Projects\tt-comics\org.fox.ttcomics\libs\nineoldandroids-2.4.0.jar:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: com.ToxicBakery.viewpager.transforms:view-pager-transforms:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.lifecycle:runtime:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-compat:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.asynclayoutinflater:asynclayoutinflater:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: com.gu:option:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:cardview-v7:28[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.interpolator:interpolator:1[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: quoin:quoin:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-base:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.slidingpanelayout:slidingpanelayout:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.media:media:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-basement:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-auth-api-phone:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: jp.co.recruit_mp:android-HeaderFooterGridView:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-vector-drawable:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-core-utils:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:recyclerview-v7:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-tasks:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-annotations:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:interpolator:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:transition:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.lifecycle:livedata:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-auth:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.appcompat:appcompat:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.legacy:legacy-support-v4:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.arch.core:core-runtime:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: org.clojure:clojure:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:drawerlayout:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-v4:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.guava:listenablefuture:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.concurrent:concurrent-futures:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.fragment:fragment:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.swiperefreshlayout:swiperefreshlayout:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: com.google.guava:guava:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: com.google.auto.service:auto-service:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:documentfile:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-auth-base:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.viewpager:viewpager:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.legacy:legacy-support-core-ui:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.loader:loader:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: frankiesardo:icepick:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:slidingpanelayout:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: scout:scout:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:appcompat-v7:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: com.github.livefront:bridge:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.activity:activity:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: ch.acra:acra:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: com.github.bumptech.glide:glide:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:collections:[email protected]ar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-livedata:[email protected]ar" level="project" />
     <orderEntry type="library" name="Gradle: frankiesardo:icepick-processor:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-basement:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.versionedparcelable:versionedparcelable:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.material:material:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.collection:collection:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.vectordrawable:vectordrawable:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: com.google.auto:auto-common:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-core-ui:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:asynclayoutinflater:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:print:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.vectordrawable:vectordrawable-animated:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.annotation:annotation:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-common:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.legacy:legacy-support-core-utils:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.print:print:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-viewmodel:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: org.clojure:core.cache:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.core:common:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:versionedparcelable:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.arch.core:core-common:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: com.shamanland:fab:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-auth:15[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.transition:transition:1[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: slingshot:slingshot:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:viewpager:[email protected]" level="project" />
     <orderEntry type="library" name="Gradle: com.nhaarman.listviewanimations:lib-core:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-auth-api-phone:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: android.arch.lifecycle:common:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:coordinatorlayout:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:customview:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:swiperefreshlayout:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-auth-base:[email protected]" level="project" />
-    <orderEntry type="library" name="Gradle: com.android.support:support-media-compat:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.recyclerview:recyclerview:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.coordinatorlayout:coordinatorlayout:[email protected]" level="project" />
+    <orderEntry type="library" name="Gradle: com.google.android.gms:play-services-tasks:[email protected]" level="project" />
   </component>
 </module>

+ 3 - 3
org.fox.ttcomics/src/main/AndroidManifest.xml

@@ -1,8 +1,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     package="org.fox.ttcomics2"
-    android:versionCode="88"
-    android:versionName="1.46" >
+    android:versionCode="89"
+    android:versionName="1.47" >
 
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.INTERNET" />
@@ -70,7 +70,7 @@
             android:exported="false"/>
 
         <provider
-            android:name="android.support.v4.content.FileProvider"
+            android:name="androidx.core.content.FileProvider"
             android:authorities="org.fox.ttcomics2.files"
             android:exported="false"
             android:grantUriPermissions="true" >

+ 39 - 0
org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/Compat.java

@@ -0,0 +1,39 @@
+/*
+ Copyright 2011, 2012 Chris Banes.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+package com.github.chrisbanes.photoview;
+
+import android.annotation.TargetApi;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.view.View;
+
+class Compat {
+
+    private static final int SIXTY_FPS_INTERVAL = 1000 / 60;
+
+    public static void postOnAnimation(View view, Runnable runnable) {
+        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
+            postOnAnimationJellyBean(view, runnable);
+        } else {
+            view.postDelayed(runnable, SIXTY_FPS_INTERVAL);
+        }
+    }
+
+    @TargetApi(16)
+    private static void postOnAnimationJellyBean(View view, Runnable runnable) {
+        view.postOnAnimation(runnable);
+    }
+}

+ 205 - 0
org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/CustomGestureDetector.java

@@ -0,0 +1,205 @@
+/*
+ Copyright 2011, 2012 Chris Banes.
+ <p/>
+ 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
+ <p/>
+ http://www.apache.org/licenses/LICENSE-2.0
+ <p/>
+ 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.github.chrisbanes.photoview;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+
+/**
+ * Does a whole lot of gesture detecting.
+ */
+class CustomGestureDetector {
+
+    private static final int INVALID_POINTER_ID = -1;
+
+    private int mActivePointerId = INVALID_POINTER_ID;
+    private int mActivePointerIndex = 0;
+    private final ScaleGestureDetector mDetector;
+
+    private VelocityTracker mVelocityTracker;
+    private boolean mIsDragging;
+    private float mLastTouchX;
+    private float mLastTouchY;
+    private final float mTouchSlop;
+    private final float mMinimumVelocity;
+    private OnGestureListener mListener;
+
+    CustomGestureDetector(Context context, OnGestureListener listener) {
+        final ViewConfiguration configuration = ViewConfiguration
+                .get(context);
+        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
+        mTouchSlop = configuration.getScaledTouchSlop();
+
+        mListener = listener;
+        ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {
+
+            @Override
+            public boolean onScale(ScaleGestureDetector detector) {
+                float scaleFactor = detector.getScaleFactor();
+
+                if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
+                    return false;
+             
+                if (scaleFactor >= 0) {
+                    mListener.onScale(scaleFactor,
+                            detector.getFocusX(), detector.getFocusY());
+                }
+                return true;
+            }
+
+            @Override
+            public boolean onScaleBegin(ScaleGestureDetector detector) {
+                return true;
+            }
+
+            @Override
+            public void onScaleEnd(ScaleGestureDetector detector) {
+                // NO-OP
+            }
+        };
+        mDetector = new ScaleGestureDetector(context, mScaleListener);
+    }
+
+    private float getActiveX(MotionEvent ev) {
+        try {
+            return ev.getX(mActivePointerIndex);
+        } catch (Exception e) {
+            return ev.getX();
+        }
+    }
+
+    private float getActiveY(MotionEvent ev) {
+        try {
+            return ev.getY(mActivePointerIndex);
+        } catch (Exception e) {
+            return ev.getY();
+        }
+    }
+
+    public boolean isScaling() {
+        return mDetector.isInProgress();
+    }
+
+    public boolean isDragging() {
+        return mIsDragging;
+    }
+
+    public boolean onTouchEvent(MotionEvent ev) {
+        try {
+            mDetector.onTouchEvent(ev);
+            return processTouchEvent(ev);
+        } catch (IllegalArgumentException e) {
+            // Fix for support lib bug, happening when onDestroy is called
+            return true;
+        }
+    }
+
+    private boolean processTouchEvent(MotionEvent ev) {
+        final int action = ev.getAction();
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN:
+                mActivePointerId = ev.getPointerId(0);
+
+                mVelocityTracker = VelocityTracker.obtain();
+                if (null != mVelocityTracker) {
+                    mVelocityTracker.addMovement(ev);
+                }
+
+                mLastTouchX = getActiveX(ev);
+                mLastTouchY = getActiveY(ev);
+                mIsDragging = false;
+                break;
+            case MotionEvent.ACTION_MOVE:
+                final float x = getActiveX(ev);
+                final float y = getActiveY(ev);
+                final float dx = x - mLastTouchX, dy = y - mLastTouchY;
+
+                if (!mIsDragging) {
+                    // Use Pythagoras to see if drag length is larger than
+                    // touch slop
+                    mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
+                }
+
+                if (mIsDragging) {
+                    mListener.onDrag(dx, dy);
+                    mLastTouchX = x;
+                    mLastTouchY = y;
+
+                    if (null != mVelocityTracker) {
+                        mVelocityTracker.addMovement(ev);
+                    }
+                }
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                mActivePointerId = INVALID_POINTER_ID;
+                // Recycle Velocity Tracker
+                if (null != mVelocityTracker) {
+                    mVelocityTracker.recycle();
+                    mVelocityTracker = null;
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                mActivePointerId = INVALID_POINTER_ID;
+                if (mIsDragging) {
+                    if (null != mVelocityTracker) {
+                        mLastTouchX = getActiveX(ev);
+                        mLastTouchY = getActiveY(ev);
+
+                        // Compute velocity within the last 1000ms
+                        mVelocityTracker.addMovement(ev);
+                        mVelocityTracker.computeCurrentVelocity(1000);
+
+                        final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker
+                                .getYVelocity();
+
+                        // If the velocity is greater than minVelocity, call
+                        // listener
+                        if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) {
+                            mListener.onFling(mLastTouchX, mLastTouchY, -vX,
+                                    -vY);
+                        }
+                    }
+                }
+
+                // Recycle Velocity Tracker
+                if (null != mVelocityTracker) {
+                    mVelocityTracker.recycle();
+                    mVelocityTracker = null;
+                }
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                final int pointerIndex = Util.getPointerIndex(ev.getAction());
+                final int pointerId = ev.getPointerId(pointerIndex);
+                if (pointerId == mActivePointerId) {
+                    // This was our active pointer going up. Choose a new
+                    // active pointer and adjust accordingly.
+                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
+                    mActivePointerId = ev.getPointerId(newPointerIndex);
+                    mLastTouchX = ev.getX(newPointerIndex);
+                    mLastTouchY = ev.getY(newPointerIndex);
+                }
+                break;
+        }
+
+        mActivePointerIndex = ev
+                .findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId
+                        : 0);
+        return true;
+    }
+}

+ 27 - 0
org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnGestureListener.java

@@ -0,0 +1,27 @@
+/*
+ Copyright 2011, 2012 Chris Banes.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+package com.github.chrisbanes.photoview;
+
+interface OnGestureListener {
+
+    void onDrag(float dx, float dy);
+
+    void onFling(float startX, float startY, float velocityX,
+                 float velocityY);
+
+    void onScale(float scaleFactor, float focusX, float focusY);
+
+}

+ 18 - 0
org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnMatrixChangedListener.java

@@ -0,0 +1,18 @@
+package com.github.chrisbanes.photoview;
+
+import android.graphics.RectF;
+
+/**
+ * Interface definition for a callback to be invoked when the internal Matrix has changed for
+ * this View.
+ */
+public interface OnMatrixChangedListener {
+
+    /**
+     * Callback for when the Matrix displaying the Drawable has changed. This could be because
+     * the View's bounds have changed, or the user has zoomed.
+     *
+     * @param rect - Rectangle displaying the Drawable's new bounds.
+     */
+    void onMatrixChanged(RectF rect);
+}

+ 14 - 0
org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnOutsidePhotoTapListener.java

@@ -0,0 +1,14 @@
+package com.github.chrisbanes.photoview;
+
+import android.widget.ImageView;
+
+/**
+ * Callback when the user tapped outside of the photo
+ */
+public interface OnOutsidePhotoTapListener {
+
+    /**
+     * The outside of the photo has been tapped
+     */
+    void onOutsidePhotoTap(ImageView imageView);
+}

+ 22 - 0
org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnPhotoTapListener.java

@@ -0,0 +1,22 @@
+package com.github.chrisbanes.photoview;
+
+import android.widget.ImageView;
+
+/**
+ * A callback to be invoked when the Photo is tapped with a single
+ * tap.
+ */
+public interface OnPhotoTapListener {
+
+    /**
+     * A callback to receive where the user taps on a photo. You will only receive a callback if
+     * the user taps on the actual photo, tapping on 'whitespace' will be ignored.
+     *
+     * @param view ImageView the user tapped.
+     * @param x    where the user tapped from the of the Drawable, as percentage of the
+     *             Drawable width.
+     * @param y    where the user tapped from the top of the Drawable, as percentage of the
+     *             Drawable height.
+     */
+    void onPhotoTap(ImageView view, float x, float y);
+}

+ 17 - 0
org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnScaleChangedListener.java

@@ -0,0 +1,17 @@
+package com.github.chrisbanes.photoview;
+
+
+/**
+ * Interface definition for callback to be invoked when attached ImageView scale changes
+ */
+public interface OnScaleChangedListener {
+
+    /**
+     * Callback for when the scale changes
+     *
+     * @param scaleFactor the scale factor (less than 1 for zoom out, greater than 1 for zoom in)
+     * @param focusX      focal point X position
+     * @param focusY      focal point Y position
+     */
+    void onScaleChange(float scaleFactor, float focusX, float focusY);
+}

+ 21 - 0
org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnSingleFlingListener.java

@@ -0,0 +1,21 @@
+package com.github.chrisbanes.photoview;
+
+import android.view.MotionEvent;
+
+/**
+ * A callback to be invoked when the ImageView is flung with a single
+ * touch
+ */
+public interface OnSingleFlingListener {
+
+    /**
+     * A callback to receive where the user flings on a ImageView. You will receive a callback if
+     * the user flings anywhere on the view.
+     *
+     * @param e1        MotionEvent the user first touch.
+     * @param e2        MotionEvent the user last touch.
+     * @param velocityX distance of user's horizontal fling.
+     * @param velocityY distance of user's vertical fling.
+     */
+    boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
+}

+ 16 - 0
org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnViewDragListener.java

@@ -0,0 +1,16 @@
+package com.github.chrisbanes.photoview;
+
+/**
+ * Interface definition for a callback to be invoked when the photo is experiencing a drag event
+ */
+public interface OnViewDragListener {
+
+    /**
+     * Callback for when the photo is experiencing a drag event. This cannot be invoked when the
+     * user is scaling.
+     *
+     * @param dx The change of the coordinates in the x-direction
+     * @param dy The change of the coordinates in the y-direction
+     */
+    void onDrag(float dx, float dy);
+}

+ 16 - 0
org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/OnViewTapListener.java

@@ -0,0 +1,16 @@
+package com.github.chrisbanes.photoview;
+
+import android.view.View;
+
+public interface OnViewTapListener {
+
+    /**
+     * A callback to receive where the user taps on a ImageView. You will receive a callback if
+     * the user taps anywhere on the view, tapping on 'whitespace' will not be ignored.
+     *
+     * @param view - View the user tapped.
+     * @param x    - where the user tapped from the left of the View.
+     * @param y    - where the user tapped from the top of the View.
+     */
+    void onViewTap(View view, float x, float y);
+}

+ 256 - 0
org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/PhotoView.java

@@ -0,0 +1,256 @@
+/*
+ Copyright 2011, 2012 Chris Banes.
+ <p>
+ 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
+ <p>
+ http://www.apache.org/licenses/LICENSE-2.0
+ <p>
+ 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.github.chrisbanes.photoview;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+
+import androidx.appcompat.widget.AppCompatImageView;
+
+/**
+ * A zoomable ImageView. See {@link PhotoViewAttacher} for most of the details on how the zooming
+ * is accomplished
+ */
[email protected]("unused")
+public class PhotoView extends AppCompatImageView {
+
+    private PhotoViewAttacher attacher;
+    private ScaleType pendingScaleType;
+
+    public PhotoView(Context context) {
+        this(context, null);
+    }
+
+    public PhotoView(Context context, AttributeSet attr) {
+        this(context, attr, 0);
+    }
+
+    public PhotoView(Context context, AttributeSet attr, int defStyle) {
+        super(context, attr, defStyle);
+        init();
+    }
+
+    private void init() {
+        attacher = new PhotoViewAttacher(this);
+        //We always pose as a Matrix scale type, though we can change to another scale type
+        //via the attacher
+        super.setScaleType(ScaleType.MATRIX);
+        //apply the previously applied scale type
+        if (pendingScaleType != null) {
+            setScaleType(pendingScaleType);
+            pendingScaleType = null;
+        }
+    }
+
+    /**
+     * Get the current {@link PhotoViewAttacher} for this view. Be wary of holding on to references
+     * to this attacher, as it has a reference to this view, which, if a reference is held in the
+     * wrong place, can cause memory leaks.
+     *
+     * @return the attacher.
+     */
+    public PhotoViewAttacher getAttacher() {
+        return attacher;
+    }
+
+    @Override
+    public ScaleType getScaleType() {
+        return attacher.getScaleType();
+    }
+
+    @Override
+    public Matrix getImageMatrix() {
+        return attacher.getImageMatrix();
+    }
+
+    @Override
+    public void setOnLongClickListener(OnLongClickListener l) {
+        attacher.setOnLongClickListener(l);
+    }
+
+    @Override
+    public void setOnClickListener(OnClickListener l) {
+        attacher.setOnClickListener(l);
+    }
+
+    @Override
+    public void setScaleType(ScaleType scaleType) {
+        if (attacher == null) {
+            pendingScaleType = scaleType;
+        } else {
+            attacher.setScaleType(scaleType);
+        }
+    }
+
+    @Override
+    public void setImageDrawable(Drawable drawable) {
+        super.setImageDrawable(drawable);
+        // setImageBitmap calls through to this method
+        if (attacher != null) {
+            attacher.update();
+        }
+    }
+
+    @Override
+    public void setImageResource(int resId) {
+        super.setImageResource(resId);
+        if (attacher != null) {
+            attacher.update();
+        }
+    }
+
+    @Override
+    public void setImageURI(Uri uri) {
+        super.setImageURI(uri);
+        if (attacher != null) {
+            attacher.update();
+        }
+    }
+
+    @Override
+    protected boolean setFrame(int l, int t, int r, int b) {
+        boolean changed = super.setFrame(l, t, r, b);
+        if (changed) {
+            attacher.update();
+        }
+        return changed;
+    }
+
+    public void setRotationTo(float rotationDegree) {
+        attacher.setRotationTo(rotationDegree);
+    }
+
+    public void setRotationBy(float rotationDegree) {
+        attacher.setRotationBy(rotationDegree);
+    }
+
+    public boolean isZoomable() {
+        return attacher.isZoomable();
+    }
+
+    public void setZoomable(boolean zoomable) {
+        attacher.setZoomable(zoomable);
+    }
+
+    public RectF getDisplayRect() {
+        return attacher.getDisplayRect();
+    }
+
+    public void getDisplayMatrix(Matrix matrix) {
+        attacher.getDisplayMatrix(matrix);
+    }
+
+    @SuppressWarnings("UnusedReturnValue") public boolean setDisplayMatrix(Matrix finalRectangle) {
+        return attacher.setDisplayMatrix(finalRectangle);
+    }
+
+    public void getSuppMatrix(Matrix matrix) {
+        attacher.getSuppMatrix(matrix);
+    }
+
+    public boolean setSuppMatrix(Matrix matrix) {
+        return attacher.setDisplayMatrix(matrix);
+    }
+
+    public float getMinimumScale() {
+        return attacher.getMinimumScale();
+    }
+
+    public float getMediumScale() {
+        return attacher.getMediumScale();
+    }
+
+    public float getMaximumScale() {
+        return attacher.getMaximumScale();
+    }
+
+    public float getScale() {
+        return attacher.getScale();
+    }
+
+    public void setAllowParentInterceptOnEdge(boolean allow) {
+        attacher.setAllowParentInterceptOnEdge(allow);
+    }
+
+    public void setMinimumScale(float minimumScale) {
+        attacher.setMinimumScale(minimumScale);
+    }
+
+    public void setMediumScale(float mediumScale) {
+        attacher.setMediumScale(mediumScale);
+    }
+
+    public void setMaximumScale(float maximumScale) {
+        attacher.setMaximumScale(maximumScale);
+    }
+
+    public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) {
+        attacher.setScaleLevels(minimumScale, mediumScale, maximumScale);
+    }
+
+    public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
+        attacher.setOnMatrixChangeListener(listener);
+    }
+
+    public void setOnPhotoTapListener(OnPhotoTapListener listener) {
+        attacher.setOnPhotoTapListener(listener);
+    }
+
+    public void setOnOutsidePhotoTapListener(OnOutsidePhotoTapListener listener) {
+        attacher.setOnOutsidePhotoTapListener(listener);
+    }
+
+    public void setOnViewTapListener(OnViewTapListener listener) {
+        attacher.setOnViewTapListener(listener);
+    }
+
+    public void setOnViewDragListener(OnViewDragListener listener) {
+        attacher.setOnViewDragListener(listener);
+    }
+
+    public void setScale(float scale) {
+        attacher.setScale(scale);
+    }
+
+    public void setScale(float scale, boolean animate) {
+        attacher.setScale(scale, animate);
+    }
+
+    public void setScale(float scale, float focalX, float focalY, boolean animate) {
+        attacher.setScale(scale, focalX, focalY, animate);
+    }
+
+    public void setZoomTransitionDuration(int milliseconds) {
+        attacher.setZoomTransitionDuration(milliseconds);
+    }
+
+    public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener onDoubleTapListener) {
+        attacher.setOnDoubleTapListener(onDoubleTapListener);
+    }
+
+    public void setOnScaleChangeListener(OnScaleChangedListener onScaleChangedListener) {
+        attacher.setOnScaleChangeListener(onScaleChangedListener);
+    }
+
+    public void setOnSingleFlingListener(OnSingleFlingListener onSingleFlingListener) {
+        attacher.setOnSingleFlingListener(onSingleFlingListener);
+    }
+}

+ 822 - 0
org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/PhotoViewAttacher.java

@@ -0,0 +1,822 @@
+/*
+ Copyright 2011, 2012 Chris Banes.
+ <p>
+ 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
+ <p>
+ http://www.apache.org/licenses/LICENSE-2.0
+ <p>
+ 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.github.chrisbanes.photoview;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Matrix.ScaleToFit;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnLongClickListener;
+import android.view.ViewParent;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import android.widget.OverScroller;
+
+/**
+ * The component of {@link PhotoView} which does the work allowing for zooming, scaling, panning, etc.
+ * It is made public in case you need to subclass something other than AppCompatImageView and still
+ * gain the functionality that {@link PhotoView} offers
+ */
+public class PhotoViewAttacher implements View.OnTouchListener,
+    View.OnLayoutChangeListener {
+
+    private static float DEFAULT_MAX_SCALE = 3.0f;
+    private static float DEFAULT_MID_SCALE = 1.75f;
+    private static float DEFAULT_MIN_SCALE = 1.0f;
+    private static int DEFAULT_ZOOM_DURATION = 200;
+
+    private static final int HORIZONTAL_EDGE_NONE = -1;
+    private static final int HORIZONTAL_EDGE_LEFT = 0;
+    private static final int HORIZONTAL_EDGE_RIGHT = 1;
+    private static final int HORIZONTAL_EDGE_BOTH = 2;
+    private static final int VERTICAL_EDGE_NONE = -1;
+    private static final int VERTICAL_EDGE_TOP = 0;
+    private static final int VERTICAL_EDGE_BOTTOM = 1;
+    private static final int VERTICAL_EDGE_BOTH = 2;
+    private static int SINGLE_TOUCH = 1;
+
+    private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
+    private int mZoomDuration = DEFAULT_ZOOM_DURATION;
+    private float mMinScale = DEFAULT_MIN_SCALE;
+    private float mMidScale = DEFAULT_MID_SCALE;
+    private float mMaxScale = DEFAULT_MAX_SCALE;
+
+    private boolean mAllowParentInterceptOnEdge = true;
+    private boolean mBlockParentIntercept = false;
+
+    private ImageView mImageView;
+
+    // Gesture Detectors
+    private GestureDetector mGestureDetector;
+    private CustomGestureDetector mScaleDragDetector;
+
+    // These are set so we don't keep allocating them on the heap
+    private final Matrix mBaseMatrix = new Matrix();
+    private final Matrix mDrawMatrix = new Matrix();
+    private final Matrix mSuppMatrix = new Matrix();
+    private final RectF mDisplayRect = new RectF();
+    private final float[] mMatrixValues = new float[9];
+
+    // Listeners
+    private OnMatrixChangedListener mMatrixChangeListener;
+    private OnPhotoTapListener mPhotoTapListener;
+    private OnOutsidePhotoTapListener mOutsidePhotoTapListener;
+    private OnViewTapListener mViewTapListener;
+    private View.OnClickListener mOnClickListener;
+    private OnLongClickListener mLongClickListener;
+    private OnScaleChangedListener mScaleChangeListener;
+    private OnSingleFlingListener mSingleFlingListener;
+    private OnViewDragListener mOnViewDragListener;
+
+    private FlingRunnable mCurrentFlingRunnable;
+    private int mHorizontalScrollEdge = HORIZONTAL_EDGE_BOTH;
+    private int mVerticalScrollEdge = VERTICAL_EDGE_BOTH;
+    private float mBaseRotation;
+
+    private boolean mZoomEnabled = true;
+    private ScaleType mScaleType = ScaleType.FIT_CENTER;
+
+    private OnGestureListener onGestureListener = new OnGestureListener() {
+        @Override
+        public void onDrag(float dx, float dy) {
+            if (mScaleDragDetector.isScaling()) {
+                return; // Do not drag if we are already scaling
+            }
+            if (mOnViewDragListener != null) {
+                mOnViewDragListener.onDrag(dx, dy);
+            }
+            mSuppMatrix.postTranslate(dx, dy);
+            checkAndDisplayMatrix();
+
+            /*
+             * Here we decide whether to let the ImageView's parent to start taking
+             * over the touch event.
+             *
+             * First we check whether this function is enabled. We never want the
+             * parent to take over if we're scaling. We then check the edge we're
+             * on, and the direction of the scroll (i.e. if we're pulling against
+             * the edge, aka 'overscrolling', let the parent take over).
+             */
+            ViewParent parent = mImageView.getParent();
+            if (mAllowParentInterceptOnEdge && !mScaleDragDetector.isScaling() && !mBlockParentIntercept) {
+                if (mHorizontalScrollEdge == HORIZONTAL_EDGE_BOTH
+                        || (mHorizontalScrollEdge == HORIZONTAL_EDGE_LEFT && dx >= 1f)
+                        || (mHorizontalScrollEdge == HORIZONTAL_EDGE_RIGHT && dx <= -1f)
+                        || (mVerticalScrollEdge == VERTICAL_EDGE_TOP && dy >= 1f)
+                        || (mVerticalScrollEdge == VERTICAL_EDGE_BOTTOM && dy <= -1f)) {
+                    if (parent != null) {
+                        parent.requestDisallowInterceptTouchEvent(false);
+                    }
+                }
+            } else {
+                if (parent != null) {
+                    parent.requestDisallowInterceptTouchEvent(true);
+                }
+            }
+        }
+
+        @Override
+        public void onFling(float startX, float startY, float velocityX, float velocityY) {
+            mCurrentFlingRunnable = new FlingRunnable(mImageView.getContext());
+            mCurrentFlingRunnable.fling(getImageViewWidth(mImageView),
+                getImageViewHeight(mImageView), (int) velocityX, (int) velocityY);
+            mImageView.post(mCurrentFlingRunnable);
+        }
+
+        @Override
+        public void onScale(float scaleFactor, float focusX, float focusY) {
+            if (getScale() < mMaxScale || scaleFactor < 1f) {
+                if (mScaleChangeListener != null) {
+                    mScaleChangeListener.onScaleChange(scaleFactor, focusX, focusY);
+                }
+                mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
+                checkAndDisplayMatrix();
+            }
+        }
+    };
+
+    public PhotoViewAttacher(ImageView imageView) {
+        mImageView = imageView;
+        imageView.setOnTouchListener(this);
+        imageView.addOnLayoutChangeListener(this);
+        if (imageView.isInEditMode()) {
+            return;
+        }
+        mBaseRotation = 0.0f;
+        // Create Gesture Detectors...
+        mScaleDragDetector = new CustomGestureDetector(imageView.getContext(), onGestureListener);
+        mGestureDetector = new GestureDetector(imageView.getContext(), new GestureDetector.SimpleOnGestureListener() {
+
+            // forward long click listener
+            @Override
+            public void onLongPress(MotionEvent e) {
+                if (mLongClickListener != null) {
+                    mLongClickListener.onLongClick(mImageView);
+                }
+            }
+
+            @Override
+            public boolean onFling(MotionEvent e1, MotionEvent e2,
+                float velocityX, float velocityY) {
+                if (mSingleFlingListener != null) {
+                    if (getScale() > DEFAULT_MIN_SCALE) {
+                        return false;
+                    }
+                    if (e1.getPointerCount() > SINGLE_TOUCH
+                        || e2.getPointerCount() > SINGLE_TOUCH) {
+                        return false;
+                    }
+                    return mSingleFlingListener.onFling(e1, e2, velocityX, velocityY);
+                }
+                return false;
+            }
+        });
+        mGestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
+            @Override
+            public boolean onSingleTapConfirmed(MotionEvent e) {
+                if (mOnClickListener != null) {
+                    mOnClickListener.onClick(mImageView);
+                }
+                final RectF displayRect = getDisplayRect();
+                final float x = e.getX(), y = e.getY();
+                if (mViewTapListener != null) {
+                    mViewTapListener.onViewTap(mImageView, x, y);
+                }
+                if (displayRect != null) {
+                    // Check to see if the user tapped on the photo
+                    if (displayRect.contains(x, y)) {
+                        float xResult = (x - displayRect.left)
+                            / displayRect.width();
+                        float yResult = (y - displayRect.top)
+                            / displayRect.height();
+                        if (mPhotoTapListener != null) {
+                            mPhotoTapListener.onPhotoTap(mImageView, xResult, yResult);
+                        }
+                        return true;
+                    } else {
+                        if (mOutsidePhotoTapListener != null) {
+                            mOutsidePhotoTapListener.onOutsidePhotoTap(mImageView);
+                        }
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public boolean onDoubleTap(MotionEvent ev) {
+                try {
+                    float scale = getScale();
+                    float x = ev.getX();
+                    float y = ev.getY();
+                    if (scale < getMediumScale()) {
+                        setScale(getMediumScale(), x, y, true);
+                    } else if (scale >= getMediumScale() && scale < getMaximumScale()) {
+                        setScale(getMaximumScale(), x, y, true);
+                    } else {
+                        setScale(getMinimumScale(), x, y, true);
+                    }
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    // Can sometimes happen when getX() and getY() is called
+                }
+                return true;
+            }
+
+            @Override
+            public boolean onDoubleTapEvent(MotionEvent e) {
+                // Wait for the confirmed onDoubleTap() instead
+                return false;
+            }
+        });
+    }
+
+    public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) {
+        this.mGestureDetector.setOnDoubleTapListener(newOnDoubleTapListener);
+    }
+
+    public void setOnScaleChangeListener(OnScaleChangedListener onScaleChangeListener) {
+        this.mScaleChangeListener = onScaleChangeListener;
+    }
+
+    public void setOnSingleFlingListener(OnSingleFlingListener onSingleFlingListener) {
+        this.mSingleFlingListener = onSingleFlingListener;
+    }
+
+    @Deprecated
+    public boolean isZoomEnabled() {
+        return mZoomEnabled;
+    }
+
+    public RectF getDisplayRect() {
+        checkMatrixBounds();
+        return getDisplayRect(getDrawMatrix());
+    }
+
+    public boolean setDisplayMatrix(Matrix finalMatrix) {
+        if (finalMatrix == null) {
+            throw new IllegalArgumentException("Matrix cannot be null");
+        }
+        if (mImageView.getDrawable() == null) {
+            return false;
+        }
+        mSuppMatrix.set(finalMatrix);
+        checkAndDisplayMatrix();
+        return true;
+    }
+
+    public void setBaseRotation(final float degrees) {
+        mBaseRotation = degrees % 360;
+        update();
+        setRotationBy(mBaseRotation);
+        checkAndDisplayMatrix();
+    }
+
+    public void setRotationTo(float degrees) {
+        mSuppMatrix.setRotate(degrees % 360);
+        checkAndDisplayMatrix();
+    }
+
+    public void setRotationBy(float degrees) {
+        mSuppMatrix.postRotate(degrees % 360);
+        checkAndDisplayMatrix();
+    }
+
+    public float getMinimumScale() {
+        return mMinScale;
+    }
+
+    public float getMediumScale() {
+        return mMidScale;
+    }
+
+    public float getMaximumScale() {
+        return mMaxScale;
+    }
+
+    public float getScale() {
+        return (float) Math.sqrt((float) Math.pow(getValue(mSuppMatrix, Matrix.MSCALE_X), 2) + (float) Math.pow
+            (getValue(mSuppMatrix, Matrix.MSKEW_Y), 2));
+    }
+
+    public ScaleType getScaleType() {
+        return mScaleType;
+    }
+
+    @Override
+    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int
+        oldRight, int oldBottom) {
+        // Update our base matrix, as the bounds have changed
+        if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
+            updateBaseMatrix(mImageView.getDrawable());
+        }
+    }
+
+    @Override
+    public boolean onTouch(View v, MotionEvent ev) {
+        boolean handled = false;
+        if (mZoomEnabled && Util.hasDrawable((ImageView) v)) {
+            switch (ev.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    ViewParent parent = v.getParent();
+                    // First, disable the Parent from intercepting the touch
+                    // event
+                    if (parent != null) {
+                        parent.requestDisallowInterceptTouchEvent(true);
+                    }
+                    // If we're flinging, and the user presses down, cancel
+                    // fling
+                    cancelFling();
+                    break;
+                case MotionEvent.ACTION_CANCEL:
+                case MotionEvent.ACTION_UP:
+                    // If the user has zoomed less than min scale, zoom back
+                    // to min scale
+                    if (getScale() < mMinScale) {
+                        RectF rect = getDisplayRect();
+                        if (rect != null) {
+                            v.post(new AnimatedZoomRunnable(getScale(), mMinScale,
+                                rect.centerX(), rect.centerY()));
+                            handled = true;
+                        }
+                    } else if (getScale() > mMaxScale) {
+                        RectF rect = getDisplayRect();
+                        if (rect != null) {
+                            v.post(new AnimatedZoomRunnable(getScale(), mMaxScale,
+                                rect.centerX(), rect.centerY()));
+                            handled = true;
+                        }
+                    }
+                    break;
+            }
+            // Try the Scale/Drag detector
+            if (mScaleDragDetector != null) {
+                boolean wasScaling = mScaleDragDetector.isScaling();
+                boolean wasDragging = mScaleDragDetector.isDragging();
+                handled = mScaleDragDetector.onTouchEvent(ev);
+                boolean didntScale = !wasScaling && !mScaleDragDetector.isScaling();
+                boolean didntDrag = !wasDragging && !mScaleDragDetector.isDragging();
+                mBlockParentIntercept = didntScale && didntDrag;
+            }
+            // Check to see if the user double tapped
+            if (mGestureDetector != null && mGestureDetector.onTouchEvent(ev)) {
+                handled = true;
+            }
+
+        }
+        return handled;
+    }
+
+    public void setAllowParentInterceptOnEdge(boolean allow) {
+        mAllowParentInterceptOnEdge = allow;
+    }
+
+    public void setMinimumScale(float minimumScale) {
+        Util.checkZoomLevels(minimumScale, mMidScale, mMaxScale);
+        mMinScale = minimumScale;
+    }
+
+    public void setMediumScale(float mediumScale) {
+        Util.checkZoomLevels(mMinScale, mediumScale, mMaxScale);
+        mMidScale = mediumScale;
+    }
+
+    public void setMaximumScale(float maximumScale) {
+        Util.checkZoomLevels(mMinScale, mMidScale, maximumScale);
+        mMaxScale = maximumScale;
+    }
+
+    public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) {
+        Util.checkZoomLevels(minimumScale, mediumScale, maximumScale);
+        mMinScale = minimumScale;
+        mMidScale = mediumScale;
+        mMaxScale = maximumScale;
+    }
+
+    public void setOnLongClickListener(OnLongClickListener listener) {
+        mLongClickListener = listener;
+    }
+
+    public void setOnClickListener(View.OnClickListener listener) {
+        mOnClickListener = listener;
+    }
+
+    public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
+        mMatrixChangeListener = listener;
+    }
+
+    public void setOnPhotoTapListener(OnPhotoTapListener listener) {
+        mPhotoTapListener = listener;
+    }
+
+    public void setOnOutsidePhotoTapListener(OnOutsidePhotoTapListener mOutsidePhotoTapListener) {
+        this.mOutsidePhotoTapListener = mOutsidePhotoTapListener;
+    }
+
+    public void setOnViewTapListener(OnViewTapListener listener) {
+        mViewTapListener = listener;
+    }
+
+    public void setOnViewDragListener(OnViewDragListener listener) {
+        mOnViewDragListener = listener;
+    }
+
+    public void setScale(float scale) {
+        setScale(scale, false);
+    }
+
+    public void setScale(float scale, boolean animate) {
+        setScale(scale,
+            (mImageView.getRight()) / 2,
+            (mImageView.getBottom()) / 2,
+            animate);
+    }
+
+    public void setScale(float scale, float focalX, float focalY,
+        boolean animate) {
+        // Check to see if the scale is within bounds
+        if (scale < mMinScale || scale > mMaxScale) {
+            throw new IllegalArgumentException("Scale must be within the range of minScale and maxScale");
+        }
+        if (animate) {
+            mImageView.post(new AnimatedZoomRunnable(getScale(), scale,
+                focalX, focalY));
+        } else {
+            mSuppMatrix.setScale(scale, scale, focalX, focalY);
+            checkAndDisplayMatrix();
+        }
+    }
+
+    /**
+     * Set the zoom interpolator
+     *
+     * @param interpolator the zoom interpolator
+     */
+    public void setZoomInterpolator(Interpolator interpolator) {
+        mInterpolator = interpolator;
+    }
+
+    public void setScaleType(ScaleType scaleType) {
+        if (Util.isSupportedScaleType(scaleType) && scaleType != mScaleType) {
+            mScaleType = scaleType;
+            update();
+        }
+    }
+
+    public boolean isZoomable() {
+        return mZoomEnabled;
+    }
+
+    public void setZoomable(boolean zoomable) {
+        mZoomEnabled = zoomable;
+        update();
+    }
+
+    public void update() {
+        if (mZoomEnabled) {
+            // Update the base matrix using the current drawable
+            updateBaseMatrix(mImageView.getDrawable());
+        } else {
+            // Reset the Matrix...
+            resetMatrix();
+        }
+    }
+
+    /**
+     * Get the display matrix
+     *
+     * @param matrix target matrix to copy to
+     */
+    public void getDisplayMatrix(Matrix matrix) {
+        matrix.set(getDrawMatrix());
+    }
+
+    /**
+     * Get the current support matrix
+     */
+    public void getSuppMatrix(Matrix matrix) {
+        matrix.set(mSuppMatrix);
+    }
+
+    private Matrix getDrawMatrix() {
+        mDrawMatrix.set(mBaseMatrix);
+        mDrawMatrix.postConcat(mSuppMatrix);
+        return mDrawMatrix;
+    }
+
+    public Matrix getImageMatrix() {
+        return mDrawMatrix;
+    }
+
+    public void setZoomTransitionDuration(int milliseconds) {
+        this.mZoomDuration = milliseconds;
+    }
+
+    /**
+     * Helper method that 'unpacks' a Matrix and returns the required value
+     *
+     * @param matrix     Matrix to unpack
+     * @param whichValue Which value from Matrix.M* to return
+     * @return returned value
+     */
+    private float getValue(Matrix matrix, int whichValue) {
+        matrix.getValues(mMatrixValues);
+        return mMatrixValues[whichValue];
+    }
+
+    /**
+     * Resets the Matrix back to FIT_CENTER, and then displays its contents
+     */
+    private void resetMatrix() {
+        mSuppMatrix.reset();
+        setRotationBy(mBaseRotation);
+        setImageViewMatrix(getDrawMatrix());
+        checkMatrixBounds();
+    }
+
+    private void setImageViewMatrix(Matrix matrix) {
+        mImageView.setImageMatrix(matrix);
+        // Call MatrixChangedListener if needed
+        if (mMatrixChangeListener != null) {
+            RectF displayRect = getDisplayRect(matrix);
+            if (displayRect != null) {
+                mMatrixChangeListener.onMatrixChanged(displayRect);
+            }
+        }
+    }
+
+    /**
+     * Helper method that simply checks the Matrix, and then displays the result
+     */
+    private void checkAndDisplayMatrix() {
+        if (checkMatrixBounds()) {
+            setImageViewMatrix(getDrawMatrix());
+        }
+    }
+
+    /**
+     * Helper method that maps the supplied Matrix to the current Drawable
+     *
+     * @param matrix - Matrix to map Drawable against
+     * @return RectF - Displayed Rectangle
+     */
+    private RectF getDisplayRect(Matrix matrix) {
+        Drawable d = mImageView.getDrawable();
+        if (d != null) {
+            mDisplayRect.set(0, 0, d.getIntrinsicWidth(),
+                d.getIntrinsicHeight());
+            matrix.mapRect(mDisplayRect);
+            return mDisplayRect;
+        }
+        return null;
+    }
+
+    /**
+     * Calculate Matrix for FIT_CENTER
+     *
+     * @param drawable - Drawable being displayed
+     */
+    private void updateBaseMatrix(Drawable drawable) {
+        if (drawable == null) {
+            return;
+        }
+        final float viewWidth = getImageViewWidth(mImageView);
+        final float viewHeight = getImageViewHeight(mImageView);
+        final int drawableWidth = drawable.getIntrinsicWidth();
+        final int drawableHeight = drawable.getIntrinsicHeight();
+        mBaseMatrix.reset();
+        final float widthScale = viewWidth / drawableWidth;
+        final float heightScale = viewHeight / drawableHeight;
+        if (mScaleType == ScaleType.CENTER) {
+            mBaseMatrix.postTranslate((viewWidth - drawableWidth) / 2F,
+                (viewHeight - drawableHeight) / 2F);
+
+        } else if (mScaleType == ScaleType.CENTER_CROP) {
+            float scale = Math.max(widthScale, heightScale);
+            mBaseMatrix.postScale(scale, scale);
+
+            /*mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
+                (viewHeight - drawableHeight * scale) / 2F);*/
+
+            // FOX: make CENTER_CROP act like TOP_CROP
+            mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
+                0);
+
+        } else if (mScaleType == ScaleType.CENTER_INSIDE) {
+            float scale = Math.min(1.0f, Math.min(widthScale, heightScale));
+            mBaseMatrix.postScale(scale, scale);
+            mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
+                (viewHeight - drawableHeight * scale) / 2F);
+
+        } else {
+            RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight);
+            RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight);
+            if ((int) mBaseRotation % 180 != 0) {
+                mTempSrc = new RectF(0, 0, drawableHeight, drawableWidth);
+            }
+            switch (mScaleType) {
+                case FIT_CENTER:
+                    mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER);
+                    break;
+                case FIT_START:
+                    mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.START);
+                    break;
+                case FIT_END:
+                    mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END);
+                    break;
+                case FIT_XY:
+                    mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.FILL);
+                    break;
+                default:
+                    break;
+            }
+        }
+        resetMatrix();
+    }
+
+    private boolean checkMatrixBounds() {
+        final RectF rect = getDisplayRect(getDrawMatrix());
+        if (rect == null) {
+            return false;
+        }
+        final float height = rect.height(), width = rect.width();
+        float deltaX = 0, deltaY = 0;
+        final int viewHeight = getImageViewHeight(mImageView);
+        if (height <= viewHeight) {
+            switch (mScaleType) {
+                case FIT_START:
+                    deltaY = -rect.top;
+                    break;
+                case FIT_END:
+                    deltaY = viewHeight - height - rect.top;
+                    break;
+                default:
+                    deltaY = (viewHeight - height) / 2 - rect.top;
+                    break;
+            }
+            mVerticalScrollEdge = VERTICAL_EDGE_BOTH;
+        } else if (rect.top > 0) {
+            mVerticalScrollEdge = VERTICAL_EDGE_TOP;
+            deltaY = -rect.top;
+        } else if (rect.bottom < viewHeight) {
+            mVerticalScrollEdge = VERTICAL_EDGE_BOTTOM;
+            deltaY = viewHeight - rect.bottom;
+        } else {
+            mVerticalScrollEdge = VERTICAL_EDGE_NONE;
+        }
+        final int viewWidth = getImageViewWidth(mImageView);
+        if (width <= viewWidth) {
+            switch (mScaleType) {
+                case FIT_START:
+                    deltaX = -rect.left;
+                    break;
+                case FIT_END:
+                    deltaX = viewWidth - width - rect.left;
+                    break;
+                default:
+                    deltaX = (viewWidth - width) / 2 - rect.left;
+                    break;
+            }
+            mHorizontalScrollEdge = HORIZONTAL_EDGE_BOTH;
+        } else if (rect.left > 0) {
+            mHorizontalScrollEdge = HORIZONTAL_EDGE_LEFT;
+            deltaX = -rect.left;
+        } else if (rect.right < viewWidth) {
+            deltaX = viewWidth - rect.right;
+            mHorizontalScrollEdge = HORIZONTAL_EDGE_RIGHT;
+        } else {
+            mHorizontalScrollEdge = HORIZONTAL_EDGE_NONE;
+        }
+        // Finally actually translate the matrix
+        mSuppMatrix.postTranslate(deltaX, deltaY);
+        return true;
+    }
+
+    private int getImageViewWidth(ImageView imageView) {
+        return imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight();
+    }
+
+    private int getImageViewHeight(ImageView imageView) {
+        return imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom();
+    }
+
+    private void cancelFling() {
+        if (mCurrentFlingRunnable != null) {
+            mCurrentFlingRunnable.cancelFling();
+            mCurrentFlingRunnable = null;
+        }
+    }
+
+    private class AnimatedZoomRunnable implements Runnable {
+
+        private final float mFocalX, mFocalY;
+        private final long mStartTime;
+        private final float mZoomStart, mZoomEnd;
+
+        public AnimatedZoomRunnable(final float currentZoom, final float targetZoom,
+            final float focalX, final float focalY) {
+            mFocalX = focalX;
+            mFocalY = focalY;
+            mStartTime = System.currentTimeMillis();
+            mZoomStart = currentZoom;
+            mZoomEnd = targetZoom;
+        }
+
+        @Override
+        public void run() {
+            float t = interpolate();
+            float scale = mZoomStart + t * (mZoomEnd - mZoomStart);
+            float deltaScale = scale / getScale();
+            onGestureListener.onScale(deltaScale, mFocalX, mFocalY);
+            // We haven't hit our target scale yet, so post ourselves again
+            if (t < 1f) {
+                Compat.postOnAnimation(mImageView, this);
+            }
+        }
+
+        private float interpolate() {
+            float t = 1f * (System.currentTimeMillis() - mStartTime) / mZoomDuration;
+            t = Math.min(1f, t);
+            t = mInterpolator.getInterpolation(t);
+            return t;
+        }
+    }
+
+    private class FlingRunnable implements Runnable {
+
+        private final OverScroller mScroller;
+        private int mCurrentX, mCurrentY;
+
+        public FlingRunnable(Context context) {
+            mScroller = new OverScroller(context);
+        }
+
+        public void cancelFling() {
+            mScroller.forceFinished(true);
+        }
+
+        public void fling(int viewWidth, int viewHeight, int velocityX,
+            int velocityY) {
+            final RectF rect = getDisplayRect();
+            if (rect == null) {
+                return;
+            }
+            final int startX = Math.round(-rect.left);
+            final int minX, maxX, minY, maxY;
+            if (viewWidth < rect.width()) {
+                minX = 0;
+                maxX = Math.round(rect.width() - viewWidth);
+            } else {
+                minX = maxX = startX;
+            }
+            final int startY = Math.round(-rect.top);
+            if (viewHeight < rect.height()) {
+                minY = 0;
+                maxY = Math.round(rect.height() - viewHeight);
+            } else {
+                minY = maxY = startY;
+            }
+            mCurrentX = startX;
+            mCurrentY = startY;
+            // If we actually can move, fling the scroller
+            if (startX != maxX || startY != maxY) {
+                mScroller.fling(startX, startY, velocityX, velocityY, minX,
+                    maxX, minY, maxY, 0, 0);
+            }
+        }
+
+        @Override
+        public void run() {
+            if (mScroller.isFinished()) {
+                return; // remaining post that should not be handled
+            }
+            if (mScroller.computeScrollOffset()) {
+                final int newX = mScroller.getCurrX();
+                final int newY = mScroller.getCurrY();
+                mSuppMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY);
+                checkAndDisplayMatrix();
+                mCurrentX = newX;
+                mCurrentY = newY;
+                // Post On animation
+                Compat.postOnAnimation(mImageView, this);
+            }
+        }
+    }
+}

+ 37 - 0
org.fox.ttcomics/src/main/java/com/github/chrisbanes/photoview/Util.java

@@ -0,0 +1,37 @@
+package com.github.chrisbanes.photoview;
+
+import android.view.MotionEvent;
+import android.widget.ImageView;
+
+class Util {
+
+    static void checkZoomLevels(float minZoom, float midZoom,
+                                float maxZoom) {
+        if (minZoom >= midZoom) {
+            throw new IllegalArgumentException(
+                    "Minimum zoom has to be less than Medium zoom. Call setMinimumZoom() with a more appropriate value");
+        } else if (midZoom >= maxZoom) {
+            throw new IllegalArgumentException(
+                    "Medium zoom has to be less than Maximum zoom. Call setMaximumZoom() with a more appropriate value");
+        }
+    }
+
+    static boolean hasDrawable(ImageView imageView) {
+        return imageView.getDrawable() != null;
+    }
+
+    static boolean isSupportedScaleType(final ImageView.ScaleType scaleType) {
+        if (scaleType == null) {
+            return false;
+        }
+        switch (scaleType) {
+            case MATRIX:
+                throw new IllegalStateException("Matrix scale type is not supported");
+        }
+        return true;
+    }
+
+    static int getPointerIndex(int action) {
+        return (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+    }
+}

+ 0 - 268
org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouch.java

@@ -1,268 +0,0 @@
-package it.sephiroth.android.library.imagezoom;
-
-import android.content.Context;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.GestureDetector.OnGestureListener;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import android.view.ScaleGestureDetector.OnScaleGestureListener;
-import android.view.ViewConfiguration;
-
-public class ImageViewTouch extends ImageViewTouchBase {
-
-	private static final float SCROLL_DELTA_THRESHOLD = 1.0f;
-	static final float MIN_ZOOM = 0.9f;
-	protected ScaleGestureDetector mScaleDetector;
-	protected GestureDetector mGestureDetector;
-	protected int mTouchSlop;
-	protected float mCurrentScaleFactor;
-	protected float mScaleFactor;
-	protected int mDoubleTapDirection;
-	protected OnGestureListener mGestureListener;
-	protected OnScaleGestureListener mScaleListener;
-	protected boolean mDoubleTapToZoomEnabled = true;
-	protected boolean mScaleEnabled = true;
-	protected boolean mScrollEnabled = true;
-
-    private OnImageViewTouchDoubleTapListener doubleTapListener;
-
-    public interface OnScaleChangedListener {
-		public void onScaleChanged(float scale);
-	}
-    
-    protected OnScaleChangedListener mScaleChangedListener;
-    
-	public ImageViewTouch( Context context, AttributeSet attrs ) {
-		super( context, attrs );
-	}
-	
-	@Override
-	protected void init() {
-		super.init();
-		mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
-		mGestureListener = getGestureListener();
-		mScaleListener = getScaleListener();
-
-		mScaleDetector = new ScaleGestureDetector( getContext(), mScaleListener );
-		mGestureDetector = new GestureDetector( getContext(), mGestureListener, null, true );
-
-		mCurrentScaleFactor = 1f;
-		mDoubleTapDirection = 1;
-	}
-
-    public void setDoubleTapListener( OnImageViewTouchDoubleTapListener doubleTapListener ){
-        this.doubleTapListener = doubleTapListener;
-    }
-
-	public void setDoubleTapToZoomEnabled( boolean value ) {
-		mDoubleTapToZoomEnabled = value;
-	}
-
-	public void setScaleEnabled( boolean value ) {
-		mScaleEnabled = value;
-	}
-
-	public void setScrollEnabled( boolean value ) {
-		mScrollEnabled = value;
-	}
-
-	public boolean getDoubleTapEnabled() {
-		return mDoubleTapToZoomEnabled;
-	}
-
-	protected OnGestureListener getGestureListener() {
-		return new GestureListener();
-	}
-
-	protected OnScaleGestureListener getScaleListener() {
-		return new ScaleListener();
-	}
-
-	@Override
-	protected void onBitmapChanged( Drawable drawable ) {
-		super.onBitmapChanged( drawable );
-
-		float v[] = new float[9];
-		mSuppMatrix.getValues( v );
-		mCurrentScaleFactor = v[Matrix.MSCALE_X];
-	}
-
-	@Override
-	protected void _setImageDrawable( final Drawable drawable, final boolean reset, final Matrix initial_matrix, final float maxZoom ) {
-		super._setImageDrawable( drawable, reset, initial_matrix, maxZoom );
-		mScaleFactor = getMaxZoom() / 3;
-	}
-
-	@Override
-	public boolean onTouchEvent( MotionEvent event ) {
-		mScaleDetector.onTouchEvent( event );
-		if ( !mScaleDetector.isInProgress() ) mGestureDetector.onTouchEvent( event );
-		int action = event.getAction();
-		switch ( action & MotionEvent.ACTION_MASK ) {
-			case MotionEvent.ACTION_UP:
-				if ( getScale() < 1f ) {
-					zoomTo( 1f, 50 );
-				}
-				break;
-		}
-		return true;
-	}
-
-	@Override
-	protected void onZoom( float scale ) {
-		super.onZoom( scale );
-		if ( !mScaleDetector.isInProgress() ) mCurrentScaleFactor = scale;
-		
-		if (mScaleChangedListener != null) {
-			mScaleChangedListener.onScaleChanged(mCurrentScaleFactor);
-		}
-	}
-
-	protected float onDoubleTapPost( float scale, float maxZoom ) {
-		if ( mDoubleTapDirection == 1 ) {
-			if (mCurrentScaleFactor - 1.0f < 0.01) { //( scale + ( mScaleFactor * 2 ) ) <= maxZoom
-
-				float scaleFactor = mScaleFactor;
-
-				RectF bitmapRect = getBitmapRect();
-				
-				float w = bitmapRect.right - bitmapRect.left;
-				float h = bitmapRect.bottom - bitmapRect.top;
-				
-				if (w < getWidth()) {
-					scaleFactor = (float)getWidth() / w - scale;
-				} else if (h < getHeight()) {
-					scaleFactor = (float)getHeight() / h - scale;
-				}
-				
-				return scale + scaleFactor;
-			} else {
-				mDoubleTapDirection = -1;
-				return maxZoom;
-			}
-		} else {
-			mDoubleTapDirection = 1;
-			return 1f;
-		}
-	}
-	
-	/**
-	 * Determines whether this ImageViewTouch can be scrolled.
-	 * @param direction
-	 * 				- positive direction value means scroll from right to left, 
-	 * 				negative value means scroll from left to right
-	 * 
-	 * @return true if there is some more place to scroll, false - otherwise.
-	 */
-	public boolean canScroll(int direction) {
-		RectF bitmapRect = getBitmapRect();
-		updateRect(bitmapRect, mScrollRect);
-		Rect imageViewRect = new Rect();
-		getGlobalVisibleRect(imageViewRect);
-		
-		if (bitmapRect.right >= imageViewRect.right) {
-			if (direction < 0) {
-				return Math.abs(bitmapRect.right - imageViewRect.right) > SCROLL_DELTA_THRESHOLD;
-			}
-		}
-		
-		double bitmapScrollRectDelta = Math.abs(bitmapRect.left - mScrollRect.left);
-		return bitmapScrollRectDelta > SCROLL_DELTA_THRESHOLD;
-	}
-	
-	public class GestureListener extends GestureDetector.SimpleOnGestureListener {
-
-		@Override
-		public boolean onDoubleTap( MotionEvent e ) {
-			Log.i( LOG_TAG, "onDoubleTap. double tap enabled? " + mDoubleTapToZoomEnabled);
-			if (mDoubleTapToZoomEnabled) {
-				float scale = getScale();
-				float targetScale = scale;
-				targetScale = onDoubleTapPost( scale, getMaxZoom() );
-				targetScale = Math.min( getMaxZoom(), Math.max( targetScale, MIN_ZOOM ) );
-				mCurrentScaleFactor = targetScale;
-				zoomTo( targetScale, e.getX(), e.getY(), 200 );
-				invalidate();
-            }
-
-            if( null != doubleTapListener ){
-                doubleTapListener.onDoubleTap();
-            }
-
-			return super.onDoubleTap( e );
-		}
-
-		@Override
-		public void onLongPress( MotionEvent e ) {
-			if ( isLongClickable() ) {
-				if ( !mScaleDetector.isInProgress() ) {
-					setPressed( true );
-					performLongClick();
-				}
-			}
-		}
-
-		@Override
-		public boolean onScroll( MotionEvent e1, MotionEvent e2, float distanceX, float distanceY ) {
-			if ( !mScrollEnabled ) return false;
-
-			if ( e1 == null || e2 == null ) return false;
-			if ( e1.getPointerCount() > 1 || e2.getPointerCount() > 1 ) return false;
-			if ( mScaleDetector.isInProgress() ) return false;
-			if ( getScale() == 1f ) return false;
-			scrollBy( -distanceX, -distanceY );
-			invalidate();
-			return super.onScroll( e1, e2, distanceX, distanceY );
-		}
-
-		@Override
-		public boolean onFling( MotionEvent e1, MotionEvent e2, float velocityX, float velocityY ) {
-			if ( !mScrollEnabled ) return false;
-
-			if ( e1.getPointerCount() > 1 || e2.getPointerCount() > 1 ) return false;
-			if ( mScaleDetector.isInProgress() ) return false;
-
-			float diffX = e2.getX() - e1.getX();
-			float diffY = e2.getY() - e1.getY();
-
-			if ( Math.abs( velocityX ) > 800 || Math.abs( velocityY ) > 800 ) {
-				scrollBy( diffX / 2, diffY / 2, 300 );
-				invalidate();
-			}
-			return super.onFling( e1, e2, velocityX, velocityY );
-		}
-	}
-
-	public class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
-
-		@SuppressWarnings("unused")
-		@Override
-		public boolean onScale( ScaleGestureDetector detector ) {
-			float span = detector.getCurrentSpan() - detector.getPreviousSpan();
-			float targetScale = mCurrentScaleFactor * detector.getScaleFactor();
-			if ( mScaleEnabled ) {
-				targetScale = Math.min( getMaxZoom(), Math.max( targetScale, MIN_ZOOM ) );
-				zoomTo( targetScale, detector.getFocusX(), detector.getFocusY() );
-				mCurrentScaleFactor = Math.min( getMaxZoom(), Math.max( targetScale, MIN_ZOOM ) );
-				mDoubleTapDirection = 1;
-				invalidate();
-				return true;
-			}
-			return false;
-		}
-	}
-
-    public interface OnImageViewTouchDoubleTapListener {
-        void onDoubleTap();
-    }
-    
-    public void setOnScaleChangedListener(OnScaleChangedListener listener) {
-    	mScaleChangedListener = listener;
-    }
-}

+ 0 - 512
org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/ImageViewTouchBase.java

@@ -1,512 +0,0 @@
-package it.sephiroth.android.library.imagezoom;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Matrix;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.widget.ImageView;
-
-import it.sephiroth.android.library.imagezoom.easing.Cubic;
-import it.sephiroth.android.library.imagezoom.easing.Easing;
-import it.sephiroth.android.library.imagezoom.graphics.FastBitmapDrawable;
-import it.sephiroth.android.library.imagezoom.utils.IDisposable;
-
-/**
- * Base View to manage image zoom/scrool/pinch operations
- * 
- * @author alessandro
- * 
- */
-public class ImageViewTouchBase extends ImageView implements IDisposable {
-
-	public interface OnBitmapChangedListener {
-
-		void onBitmapChanged( Drawable drawable );
-	}
-
-	public static final String LOG_TAG = "image";
-
-	protected Easing mEasing = new Cubic();
-	protected Matrix mBaseMatrix = new Matrix();
-	protected Matrix mSuppMatrix = new Matrix();
-	protected Handler mHandler = new Handler();
-	protected Runnable mOnLayoutRunnable = null;
-	protected float mMaxZoom;
-	protected final Matrix mDisplayMatrix = new Matrix();
-	protected final float[] mMatrixValues = new float[9];
-	protected int mThisWidth = -1, mThisHeight = -1;
-	protected boolean mFitToScreen = false;
-	protected boolean mFitToWidth = false;
-	final protected float MAX_ZOOM = 5.0f;
-
-	protected RectF mBitmapRect = new RectF();
-	protected RectF mCenterRect = new RectF();
-	protected RectF mScrollRect = new RectF();
-
-	private OnBitmapChangedListener mListener;
-
-	public ImageViewTouchBase( Context context ) {
-		super( context );
-		init();
-	}
-
-	public ImageViewTouchBase( Context context, AttributeSet attrs ) {
-		super( context, attrs );
-		init();
-	}
-
-	public void setOnBitmapChangedListener( OnBitmapChangedListener listener ) {
-		mListener = listener;
-	}
-
-	protected void init() {
-		setScaleType( ImageView.ScaleType.MATRIX );
-	}
-
-	public void clear() {
-		setImageBitmap( null, true );
-	}
-
-	public void setFitToScreen( boolean value ) {
-		if ( value != mFitToScreen ) {
-			mFitToScreen = value;
-			requestLayout();
-		}
-	}
-
-	public void setFitToWidth( boolean value ) {
-		if ( value != mFitToWidth ) {
-			mFitToWidth = value;
-			requestLayout();
-		}
-	}
-
-	@Override
-	protected void onLayout( boolean changed, int left, int top, int right, int bottom ) {
-		super.onLayout( changed, left, top, right, bottom );
-		mThisWidth = right - left;
-		mThisHeight = bottom - top;
-		Runnable r = mOnLayoutRunnable;
-		if ( r != null ) {
-			mOnLayoutRunnable = null;
-			r.run();
-		}
-		if ( getDrawable() != null ) {
-			if (mFitToWidth) {
-				zoomToWidth();
-			} else {
-				if (mFitToScreen)
-					getProperBaseMatrix2(getDrawable(), mBaseMatrix);
-				else
-					getProperBaseMatrix(getDrawable(), mBaseMatrix);
-				setImageMatrix(getImageViewMatrix());
-			}
-		}
-	}
-
-	@Override
-	public void setImageBitmap( Bitmap bm ) {
-		setImageBitmap( bm, true );
-	}
-	
-	@Override
-	public void setImageResource( int resId ) {
-		setImageDrawable( getContext().getResources().getDrawable( resId ) );
-	}
-
-	/**
-	 * Set the new image to display and reset the internal matrix.
-	 * 
-	 * @param bitmap
-	 *           - the {@link Bitmap} to display
-	 * @param reset
-	 *           - if true the image bounds will be recreated, otherwise the old {@link Matrix} is used to display the new bitmap
-	 * @see #setImageBitmap(Bitmap)
-	 */
-	public void setImageBitmap( final Bitmap bitmap, final boolean reset ) {
-		setImageBitmap( bitmap, reset, null );
-	}
-
-	/**
-	 * Similar to {@link #setImageBitmap(Bitmap, boolean)} but an optional view {@link Matrix} can be passed to determine the new
-	 * bitmap view matrix.<br />
-	 * This method is useful if you need to restore a Bitmap with the same zoom/pan values from a previous state
-	 * 
-	 * @param bitmap
-	 *           - the {@link Bitmap} to display
-	 * @param reset
-	 * @param matrix
-	 *           - the {@link Matrix} to be used to display the new bitmap
-	 * 
-	 * @see #setImageBitmap(Bitmap, boolean)
-	 * @see #setImageBitmap(Bitmap)
-	 * @see #getImageViewMatrix()
-	 * @see #getDisplayMatrix()
-	 */
-	public void setImageBitmap( final Bitmap bitmap, final boolean reset, Matrix matrix ) {
-		setImageBitmap( bitmap, reset, matrix, -1 );
-	}
-
-	/**
-	 * 
-	 * @param bitmap
-	 * @param reset
-	 * @param matrix
-	 * @param maxZoom
-	 *           - maximum zoom allowd during zoom gestures
-	 * 
-	 * @see #setImageBitmap(Bitmap, boolean, Matrix)
-	 */
-	public void setImageBitmap( final Bitmap bitmap, final boolean reset, Matrix matrix, float maxZoom ) {
-
-		Log.i( LOG_TAG, "setImageBitmap: " + bitmap );
-
-		if ( bitmap != null )
-			setImageDrawable( new FastBitmapDrawable( bitmap ), reset, matrix, maxZoom );
-		else
-			setImageDrawable( null, reset, matrix, maxZoom );
-	}
-
-	@Override
-	public void setImageDrawable( Drawable drawable ) {
-		setImageDrawable( drawable, true, null, -1 );
-	}
-
-	public void setImageDrawable( final Drawable drawable, final boolean reset, final Matrix initial_matrix, final float maxZoom ) {
-
-		final int viewWidth = getWidth();
-
-		if ( viewWidth <= 0 ) {
-			mOnLayoutRunnable = new Runnable() {
-
-				@Override
-				public void run() {
-					setImageDrawable( drawable, reset, initial_matrix, maxZoom );
-				}
-			};
-			return;
-		}
-
-		_setImageDrawable( drawable, reset, initial_matrix, maxZoom );
-	}
-
-	protected void _setImageDrawable( final Drawable drawable, final boolean reset, final Matrix initial_matrix, final float maxZoom ) {
-		
-		if ( drawable != null ) {
-			if ( mFitToScreen )
-				getProperBaseMatrix2( drawable, mBaseMatrix );
-			else
-				getProperBaseMatrix( drawable, mBaseMatrix );
-			super.setImageDrawable( drawable );
-			
-			if (mFitToWidth) zoomToWidth();
-			
-		} else {
-			mBaseMatrix.reset();
-			super.setImageDrawable( null );
-		}
-
-		if ( reset ) {
-			mSuppMatrix.reset();
-			if ( initial_matrix != null ) {
-				mSuppMatrix = new Matrix( initial_matrix );
-			}
-		}
-
-		setImageMatrix( getImageViewMatrix() );
-
-		if ( maxZoom < 1 )
-			mMaxZoom = maxZoom();
-		else
-			mMaxZoom = maxZoom;
-
-		onBitmapChanged( drawable );
-	}
-
-	protected void onBitmapChanged( final Drawable bitmap ) {
-		if ( mListener != null ) {
-			mListener.onBitmapChanged( bitmap );
-		}
-	}
-
-	protected float maxZoom() {
-		final Drawable drawable = getDrawable();
-
-		if ( drawable == null ) {
-			return 1F;
-		}
-
-		float fw = (float) drawable.getIntrinsicWidth() / (float) mThisWidth;
-		float fh = (float) drawable.getIntrinsicHeight() / (float) mThisHeight;
-		float max = Math.max( fw, fh ) * 4;
-		return max;
-	}
-
-	public float getMaxZoom() {
-		return mMaxZoom;
-	}
-
-	public Matrix getImageViewMatrix() {
-		mDisplayMatrix.set( mBaseMatrix );
-		mDisplayMatrix.postConcat( mSuppMatrix );
-		return mDisplayMatrix;
-	}
-
-	/**
-	 * Returns the current image display matrix. This matrix can be used in the next call to the
-	 * {@link #setImageBitmap(Bitmap, boolean, Matrix)} to restore the same view state of the previous {@link Bitmap}
-	 * 
-	 * @return
-	 */
-	public Matrix getDisplayMatrix() {
-		return new Matrix( mSuppMatrix );
-	}
-
-	/**
-	 * Setup the base matrix so that the image is centered and scaled properly.
-	 * 
-	 * @param bitmap
-	 * @param matrix
-	 */
-	protected void getProperBaseMatrix( Drawable drawable, Matrix matrix ) {
-		float viewWidth = getWidth();
-		float viewHeight = getHeight();
-		float w = drawable.getIntrinsicWidth();
-		float h = drawable.getIntrinsicHeight();
-		matrix.reset();
-
-		if ( w > viewWidth || h > viewHeight ) {
-			float widthScale = Math.min( viewWidth / w, 2.0f );
-			float heightScale = Math.min( viewHeight / h, 2.0f );
-			float scale = Math.min( widthScale, heightScale );
-			matrix.postScale( scale, scale );
-			float tw = ( viewWidth - w * scale ) / 2.0f;
-			float th = ( viewHeight - h * scale ) / 2.0f;
-			matrix.postTranslate( tw, th );
-		} else {
-			float tw = ( viewWidth - w ) / 2.0f;
-			float th = ( viewHeight - h ) / 2.0f;
-			matrix.postTranslate( tw, th );
-		}
-	}
-
-	/**
-	 * Setup the base matrix so that the image is centered and scaled properly.
-	 * 
-	 * @param bitmap
-	 * @param matrix
-	 */
-	protected void getProperBaseMatrix2( Drawable bitmap, Matrix matrix ) {
-		float viewWidth = getWidth();
-		float viewHeight = getHeight();
-		float w = bitmap.getIntrinsicWidth();
-		float h = bitmap.getIntrinsicHeight();
-		matrix.reset();
-		float widthScale = Math.min( viewWidth / w, MAX_ZOOM );
-		float heightScale = Math.min( viewHeight / h, MAX_ZOOM );
-		float scale = Math.min( widthScale, heightScale );
-		matrix.postScale( scale, scale );
-		matrix.postTranslate( ( viewWidth - w * scale ) / 2.0f, ( viewHeight - h * scale ) / 2.0f );
-	}
-
-	protected float getValue( Matrix matrix, int whichValue ) {
-		matrix.getValues( mMatrixValues );
-		return mMatrixValues[whichValue];
-	}
-
-	protected RectF getBitmapRect() {
-		final Drawable drawable = getDrawable();
-
-		if ( drawable == null ) return null;
-		Matrix m = getImageViewMatrix();
-		mBitmapRect.set( 0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight() );
-		m.mapRect( mBitmapRect );
-		return mBitmapRect;
-	}
-
-	protected float getScale( Matrix matrix ) {
-		return getValue( matrix, Matrix.MSCALE_X );
-	}
-
-	public float getRotation() {
-		return 0;
-	}
-
-	public float getScale() {
-		return getScale( mSuppMatrix );
-	}
-
-	protected void center( boolean horizontal, boolean vertical ) {
-		// Log.i(LOG_TAG, "center");
-		final Drawable drawable = getDrawable();
-
-		if ( drawable == null ) return;
-		RectF rect = getCenter( horizontal, vertical );
-		if ( rect.left != 0 || rect.top != 0 ) {
-			postTranslate( rect.left, rect.top );
-		}
-	}
-
-	protected RectF getCenter( boolean horizontal, boolean vertical ) {
-		final Drawable drawable = getDrawable();
-
-		if ( drawable == null ) return new RectF( 0, 0, 0, 0 );
-
-		RectF rect = getBitmapRect();
-		float height = rect.height();
-		float width = rect.width();
-		float deltaX = 0, deltaY = 0;
-		if ( vertical ) {
-			int viewHeight = getHeight();
-			if ( height < viewHeight ) {
-				deltaY = ( viewHeight - height ) / 2 - rect.top;
-			} else if ( rect.top > 0 ) {
-				deltaY = -rect.top;
-			} else if ( rect.bottom < viewHeight ) {
-				deltaY = getHeight() - rect.bottom;
-			}
-		}
-		if ( horizontal ) {
-			int viewWidth = getWidth();
-			if ( width < viewWidth ) {
-				deltaX = ( viewWidth - width ) / 2 - rect.left;
-			} else if ( rect.left > 0 ) {
-				deltaX = -rect.left;
-			} else if ( rect.right < viewWidth ) {
-				deltaX = viewWidth - rect.right;
-			}
-		}
-		mCenterRect.set( deltaX, deltaY, 0, 0 );
-		return mCenterRect;
-	}
-
-	protected void postTranslate( float deltaX, float deltaY ) {
-		mSuppMatrix.postTranslate( deltaX, deltaY );
-		setImageMatrix( getImageViewMatrix() );
-	}
-
-	protected void postScale( float scale, float centerX, float centerY ) {
-		mSuppMatrix.postScale( scale, scale, centerX, centerY );
-		setImageMatrix( getImageViewMatrix() );
-	}
-
-	protected void zoomTo( float scale ) {
-		float cx = getWidth() / 2F;
-		float cy = getHeight() / 2F;
-		zoomTo( scale, cx, cy );
-	}
-
-	public void zoomTo( float scale, float durationMs ) {
-		float cx = getWidth() / 2F;
-		float cy = getHeight() / 2F;
-		zoomTo( scale, cx, cy, durationMs );
-	}
-
-	protected void zoomTo( float scale, float centerX, float centerY ) {
-		if ( scale > mMaxZoom ) scale = mMaxZoom;
-		float oldScale = getScale();
-		float deltaScale = scale / oldScale;
-		postScale( deltaScale, centerX, centerY );
-		onZoom( getScale() );
-		center( true, true );
-	}
-
-	protected void onZoom( float scale ) {}
-
-	public void scrollBy( float x, float y ) {
-		panBy( x, y );
-	}
-
-	protected void panBy( double dx, double dy ) {
-		RectF rect = getBitmapRect();
-		mScrollRect.set( (float) dx, (float) dy, 0, 0 );
-		updateRect( rect, mScrollRect );
-		postTranslate( mScrollRect.left, mScrollRect.top );
-		center( true, true );
-	}
-
-	protected void updateRect( RectF bitmapRect, RectF scrollRect ) {
-		float width = getWidth();
-		float height = getHeight();
-
-		if ( bitmapRect.top >= 0 && bitmapRect.bottom <= height ) scrollRect.top = 0;
-		if ( bitmapRect.left >= 0 && bitmapRect.right <= width ) scrollRect.left = 0;
-		if ( bitmapRect.top + scrollRect.top >= 0 && bitmapRect.bottom > height ) scrollRect.top = (int) ( 0 - bitmapRect.top );
-		if ( bitmapRect.bottom + scrollRect.top <= ( height - 0 ) && bitmapRect.top < 0 )
-			scrollRect.top = (int) ( ( height - 0 ) - bitmapRect.bottom );
-		if ( bitmapRect.left + scrollRect.left >= 0 ) scrollRect.left = (int) ( 0 - bitmapRect.left );
-		if ( bitmapRect.right + scrollRect.left <= ( width - 0 ) ) scrollRect.left = (int) ( ( width - 0 ) - bitmapRect.right );
-		// Log.d( LOG_TAG, "scrollRect(2): " + scrollRect.toString() );
-	}
-
-	protected void scrollBy( float distanceX, float distanceY, final double durationMs ) {
-		final double dx = distanceX;
-		final double dy = distanceY;
-		final long startTime = System.currentTimeMillis();
-		mHandler.post( new Runnable() {
-
-			double old_x = 0;
-			double old_y = 0;
-
-			@Override
-			public void run() {
-				long now = System.currentTimeMillis();
-				double currentMs = Math.min( durationMs, now - startTime );
-				double x = mEasing.easeOut( currentMs, 0, dx, durationMs );
-				double y = mEasing.easeOut( currentMs, 0, dy, durationMs );
-				panBy( ( x - old_x ), ( y - old_y ) );
-				old_x = x;
-				old_y = y;
-				if ( currentMs < durationMs ) {
-					mHandler.post( this );
-				} else {
-					RectF centerRect = getCenter( true, true );
-					if ( centerRect.left != 0 || centerRect.top != 0 ) scrollBy( centerRect.left, centerRect.top );
-				}
-			}
-		} );
-	}
-
-	protected void zoomTo( float scale, final float centerX, final float centerY, final float durationMs ) {
-		// Log.i( LOG_TAG, "zoomTo: " + scale + ", " + centerX + ": " + centerY );
-		final long startTime = System.currentTimeMillis();
-		final float incrementPerMs = ( scale - getScale() ) / durationMs;
-		final float oldScale = getScale();
-		mHandler.post( new Runnable() {
-
-			@Override
-			public void run() {
-				long now = System.currentTimeMillis();
-				float currentMs = Math.min( durationMs, now - startTime );
-				float target = oldScale + ( incrementPerMs * currentMs );
-				zoomTo( target, centerX, centerY );
-				if ( currentMs < durationMs ) {
-					mHandler.post( this );
-				} else {
-					// if ( getScale() < 1f ) {}
-				}
-			}
-		} );
-	}
-
-	@Override
-	public void dispose() {
-		clear();
-	}
-	
-	protected void zoomToWidth() {
-		RectF bitmapRect = getBitmapRect();
-		
-		float w = bitmapRect.right - bitmapRect.left;
-		
-		if (w < getWidth()) {
-			float scale = (float)getWidth() / w;
-			
-			zoomTo(scale, 0f, 0f);
-		}
-	}
-}

+ 0 - 20
org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/easing/Cubic.java

@@ -1,20 +0,0 @@
-package it.sephiroth.android.library.imagezoom.easing;
-
-public class Cubic implements Easing {
-
-	@Override
-	public double easeOut( double time, double start, double end, double duration ) {
-		return end * ( ( time = time / duration - 1.0 ) * time * time + 1.0 ) + start;
-	}
-
-	@Override
-	public double easeIn( double time, double start, double end, double duration ) {
-		return end * ( time /= duration ) * time * time + start;
-	}
-
-	@Override
-	public double easeInOut( double time, double start, double end, double duration ) {
-		if ( ( time /= duration / 2.0 ) < 1.0 ) return end / 2.0 * time * time * time + start;
-		return end / 2.0 * ( ( time -= 2.0 ) * time * time + 2.0 ) + start;
-	}
-}

+ 0 - 10
org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/easing/Easing.java

@@ -1,10 +0,0 @@
-package it.sephiroth.android.library.imagezoom.easing;
-
-public interface Easing {
-
-	double easeOut( double time, double start, double end, double duration );
-
-	double easeIn( double time, double start, double end, double duration );
-
-	double easeInOut( double time, double start, double end, double duration );
-}

+ 0 - 84
org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/graphics/FastBitmapDrawable.java

@@ -1,84 +0,0 @@
-package it.sephiroth.android.library.imagezoom.graphics;
-
-import java.io.InputStream;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
-
-/**
- * Fast bitmap drawable. Does not support states. it only
- * support alpha and colormatrix
- * @author alessandro
- *
- */
-public class FastBitmapDrawable extends Drawable implements IBitmapDrawable {
-
-	protected Bitmap mBitmap;
-	protected Paint mPaint;
-
-	public FastBitmapDrawable( Bitmap b ) {
-		mBitmap = b;
-		mPaint = new Paint();
-		mPaint.setDither( true );
-		mPaint.setFilterBitmap( true );
-	}
-	
-	public FastBitmapDrawable( Resources res, InputStream is ){
-		this(BitmapFactory.decodeStream(is));
-	}
-
-	@Override
-	public void draw( Canvas canvas ) {
-		canvas.drawBitmap( mBitmap, 0.0f, 0.0f, mPaint );
-	}
-
-	@Override
-	public int getOpacity() {
-		return PixelFormat.TRANSLUCENT;
-	}
-
-	@Override
-	public void setAlpha( int alpha ) {
-		mPaint.setAlpha( alpha );
-	}
-
-	@Override
-	public void setColorFilter( ColorFilter cf ) {
-		mPaint.setColorFilter( cf );
-	}
-
-	@Override
-	public int getIntrinsicWidth() {
-		return mBitmap.getWidth();
-	}
-
-	@Override
-	public int getIntrinsicHeight() {
-		return mBitmap.getHeight();
-	}
-
-	@Override
-	public int getMinimumWidth() {
-		return mBitmap.getWidth();
-	}
-
-	@Override
-	public int getMinimumHeight() {
-		return mBitmap.getHeight();
-	}
-	
-	public void setAntiAlias( boolean value ){
-		mPaint.setAntiAlias( value );
-		invalidateSelf();
-	}
-
-	@Override
-	public Bitmap getBitmap() {
-		return mBitmap;
-	}
-}

+ 0 - 14
org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/graphics/IBitmapDrawable.java

@@ -1,14 +0,0 @@
-package it.sephiroth.android.library.imagezoom.graphics;
-
-import it.sephiroth.android.library.imagezoom.ImageViewTouchBase;
-import android.graphics.Bitmap;
-
-/**
- * Base interface used in the {@link ImageViewTouchBase} view
- * @author alessandro
- *
- */
-public interface IBitmapDrawable {
-
-	Bitmap getBitmap();
-}

+ 0 - 6
org.fox.ttcomics/src/main/java/it/sephiroth/android/library/imagezoom/utils/IDisposable.java

@@ -1,6 +0,0 @@
-package it.sephiroth.android.library.imagezoom.utils;
-
-public interface IDisposable {
-
-	void dispose();
-}

+ 2 - 2
org.fox.ttcomics/src/main/java/org/fox/ttcomics2/Application.java

@@ -1,8 +1,6 @@
 package org.fox.ttcomics2;
 
 import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
 
 import com.livefront.bridge.Bridge;
 import com.livefront.bridge.SavedStateHandler;
@@ -11,6 +9,8 @@ import org.acra.ACRA;
 import org.acra.ReportingInteractionMode;
 import org.acra.annotation.ReportsCrashes;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import icepick.Icepick;
 
 @ReportsCrashes(mode = ReportingInteractionMode.SILENT,

+ 35 - 145
org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicFragment.java

@@ -5,22 +5,22 @@ import android.app.Activity;
 import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.preference.PreferenceManager;
-import android.support.v7.app.ActionBar;
-import android.view.GestureDetector;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.ImageView;
 
 import com.bumptech.glide.Glide;
 import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.github.chrisbanes.photoview.OnViewTapListener;
+import com.github.chrisbanes.photoview.PhotoView;
 
 import org.fox.ttcomics2.archive.ComicArchive;
 
 import java.io.IOException;
 
+import androidx.appcompat.app.ActionBar;
 import icepick.State;
-import it.sephiroth.android.library.imagezoom.ImageViewTouch;
 
 public class ComicFragment extends StateSavedFragment {
 	private final String TAG = this.getClass().getSimpleName();
@@ -28,9 +28,8 @@ public class ComicFragment extends StateSavedFragment {
 	private SharedPreferences m_prefs;
 	@State protected int m_page;
 	private ViewComicActivity m_activity;
-	private GestureDetector m_detector;
-	@State protected boolean m_thumbnail = false;
     @State protected ComicArchive m_archive;
+    private PhotoView m_image;
 	
 	public ComicFragment() {
 		super();
@@ -45,147 +44,37 @@ public class ComicFragment extends StateSavedFragment {
 	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {    	
 		
 		View view = inflater.inflate(R.layout.fragment_comic, container, false);
-		
-		final ImageViewTouch image = view.findViewById(R.id.comic_image);
 
-		if (m_prefs.getBoolean("fit_to_width", false)) {
-			image.setFitToWidth(true);
-		} else {
-			image.setFitToScreen(true);
-		}
+		m_image = view.findViewById(R.id.comic_image);
 
 		try {
-			Glide.with(ComicFragment.this)
+			Glide.with(getContext())
                 .load(m_archive.getByteArray(m_page))
 				.dontAnimate()
+				.dontTransform()
 				.diskCacheStrategy(DiskCacheStrategy.NONE)
 				.skipMemoryCache(true)
-                .into(image);
+                .into(m_image);
 		} catch (IOException e) {
-			image.setImageResource(R.drawable.badimage);
+			m_image.setImageResource(R.drawable.badimage);
 			e.printStackTrace();
 		}
 
-        image.setOnScaleChangedListener(new ImageViewTouch.OnScaleChangedListener() {
-            @Override
-            public void onScaleChanged(float scale) {
-                // TODO: shared scale change?
-            }
-        });
-
-        image.setOnTouchListener(new View.OnTouchListener() {
-            @Override
-            public boolean onTouch(View view, MotionEvent event) {
-                return m_detector.onTouchEvent(event);
-            }
-        });
-			
-		//}
-		
-		return view;
-		
-	}
-	
-	private void onLeftSideTapped() {
-		ImageViewTouch image = getView().findViewById(R.id.comic_image);
-		
-		if (image != null) {
-			boolean atLeftEdge = !image.canScroll(1);
-			
-			if (atLeftEdge) {
-				m_activity.selectPreviousComic();
-			}
-		}
-	}
-
-	private void onRightSideTapped() {
-		ImageViewTouch image = getView().findViewById(R.id.comic_image);
-		
-		if (image != null) {
-			boolean atRightEdge = !image.canScroll(-1);
-			
-			if (atRightEdge) {
-				m_activity.selectNextComic();
-			}
+		if (m_prefs.getBoolean("fit_to_width", false)) {
+			m_image.setScaleType(ImageView.ScaleType.CENTER_CROP);
 		}
-	}
 
-	@Override
-	public void setUserVisibleHint(boolean isVisibleToUser) {
-		super.setUserVisibleHint(isVisibleToUser);
-		
-		//setThumbnail(!isVisibleToUser); disabled
-	}
-	
-	@Override
-	public void onAttach(Activity activity) {
-		super.onAttach(activity);
-		
-		m_prefs = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext());
-		m_activity = (ViewComicActivity) activity;
-		
-		m_detector = new GestureDetector(m_activity, new GestureDetector.OnGestureListener() {
-			
-			@Override
-			public boolean onSingleTapUp(MotionEvent e) {
-				int width = getView().getWidth();
-				int x = Math.round(e.getX());
-
-				if (x <= width/10) {
-					onLeftSideTapped();
-
-					return true;
-				} else if (x >= width-(width/10)) {
-					onRightSideTapped();
-
-					return true;
-				}
-
-				return false;
-