From 54e3738c2d0ec149cda0acef34bd54077b5c55a9 Mon Sep 17 00:00:00 2001 From: ggtlvkma356 <265052551+ggtlvkma356@users.noreply.github.com> Date: Mon, 30 Mar 2026 18:27:13 +0200 Subject: [PATCH 1/2] Allow jumping to previous/next page with horizontal swipe gesture --- .../grapheneos/pdfviewer/GestureHelper.java | 9 ++++ .../app/grapheneos/pdfviewer/PdfViewer.java | 41 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/app/src/main/java/app/grapheneos/pdfviewer/GestureHelper.java b/app/src/main/java/app/grapheneos/pdfviewer/GestureHelper.java index e87590f12..a5965c996 100644 --- a/app/src/main/java/app/grapheneos/pdfviewer/GestureHelper.java +++ b/app/src/main/java/app/grapheneos/pdfviewer/GestureHelper.java @@ -7,6 +7,9 @@ import android.view.ScaleGestureDetector; import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + /* The GestureHelper present a simple gesture api for the PdfViewer */ @@ -14,6 +17,7 @@ class GestureHelper { public interface GestureListener { boolean onTapUp(); + boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY); void onZoom(float scaleFactor, float focusX, float focusY); void onZoomEnd(); } @@ -27,6 +31,11 @@ static void attach(Context context, View gestureView, GestureListener listener) public boolean onSingleTapUp(MotionEvent motionEvent) { return listener.onTapUp(); } + + @Override + public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) { + return listener.onFling(e1, e2, velocityX, velocityY); + } }); final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, diff --git a/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java b/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java index 936f70851..d43e9fa43 100644 --- a/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java +++ b/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java @@ -13,6 +13,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; import android.webkit.CookieManager; import android.webkit.JavascriptInterface; @@ -28,6 +29,7 @@ import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.core.view.WindowCompat; import androidx.fragment.app.Fragment; @@ -117,6 +119,8 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader private static final int STATE_LOADED = 1; private static final int STATE_END = 2; private static final int PADDING = 10; + private static final int SWIPE_THRESHOLD = 80; + private static final int SWIPE_VELOCITY_THRESHOLD = 100; private boolean webViewCrashed; private Uri mUri; @@ -450,6 +454,43 @@ public boolean onTapUp() { return false; } + @Override + public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) { + if (e1 == null) return false; + + float deltaX = e2.getX() - e1.getX(); + float deltaY = e2.getY() - e1.getY(); + float absDeltaX = Math.abs(deltaX); + float absDeltaY = Math.abs(deltaY); + + float screenDensity = getResources().getDisplayMetrics().density; + int swipeThreshold = (int) (SWIPE_THRESHOLD * screenDensity); + int swipeVelocityThreshold = (int) (SWIPE_VELOCITY_THRESHOLD * screenDensity); + + // Check primarily horizontal + if (absDeltaX > absDeltaY && + absDeltaX > swipeThreshold && + Math.abs(velocityX) > swipeVelocityThreshold) { + + boolean swipeLeft = deltaX < 0; + boolean swipeRight = deltaX > 0; + + // Edge detection + boolean atLeftEdge = !binding.webview.canScrollHorizontally(-1); + boolean atRightEdge = !binding.webview.canScrollHorizontally(1); + + if (swipeLeft && atRightEdge) { + onJumpToPageInDocument(mPage + 1); + return true; + } else if (swipeRight && atLeftEdge) { + onJumpToPageInDocument(mPage - 1); + return true; + } + } + + return false; + } + @Override public void onZoom(float scaleFactor, float focusX, float focusY) { zoom(scaleFactor, focusX, focusY, false); From b639cf6ae8846b0b3f638b2c1dd07c34c5459743 Mon Sep 17 00:00:00 2001 From: ggtlvkma356 <265052551+ggtlvkma356@users.noreply.github.com> Date: Mon, 30 Mar 2026 19:04:54 +0200 Subject: [PATCH 2/2] Prevent page change during zoom; optimize thresholds --- .../grapheneos/pdfviewer/GestureHelper.java | 36 ++++++++++++------- .../app/grapheneos/pdfviewer/PdfViewer.java | 17 +++++---- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/app/grapheneos/pdfviewer/GestureHelper.java b/app/src/main/java/app/grapheneos/pdfviewer/GestureHelper.java index a5965c996..cf505dcd6 100644 --- a/app/src/main/java/app/grapheneos/pdfviewer/GestureHelper.java +++ b/app/src/main/java/app/grapheneos/pdfviewer/GestureHelper.java @@ -25,19 +25,6 @@ public interface GestureListener { @SuppressLint("ClickableViewAccessibility") static void attach(Context context, View gestureView, GestureListener listener) { - final GestureDetector detector = new GestureDetector(context, - new GestureDetector.SimpleOnGestureListener() { - @Override - public boolean onSingleTapUp(MotionEvent motionEvent) { - return listener.onTapUp(); - } - - @Override - public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2, float velocityX, float velocityY) { - return listener.onFling(e1, e2, velocityX, velocityY); - } - }); - final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() { @Override @@ -54,6 +41,29 @@ public void onScaleEnd(ScaleGestureDetector detector) { } }); + final GestureDetector detector = new GestureDetector(context, + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onSingleTapUp(@NonNull MotionEvent motionEvent) { + return listener.onTapUp(); + } + + @Override + public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2, + float velocityX, float velocityY) { + if (scaleDetector.isInProgress()) { + return false; + } + + // Ignore multi-touch + if (e1 != null && (e1.getPointerCount() > 1 || e2.getPointerCount() > 1)) { + return false; + } + + return listener.onFling(e1, e2, velocityX, velocityY); + } + }); + gestureView.setOnTouchListener((view, motionEvent) -> { detector.onTouchEvent(motionEvent); scaleDetector.onTouchEvent(motionEvent); diff --git a/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java b/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java index d43e9fa43..0ac63fb19 100644 --- a/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java +++ b/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java @@ -15,6 +15,7 @@ import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; +import android.view.ViewConfiguration; import android.webkit.CookieManager; import android.webkit.JavascriptInterface; import android.webkit.RenderProcessGoneDetail; @@ -119,8 +120,6 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader private static final int STATE_LOADED = 1; private static final int STATE_END = 2; private static final int PADDING = 10; - private static final int SWIPE_THRESHOLD = 80; - private static final int SWIPE_VELOCITY_THRESHOLD = 100; private boolean webViewCrashed; private Uri mUri; @@ -129,6 +128,8 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader private float mZoomRatio = 1f; private float mZoomFocusX = 0f; private float mZoomFocusY = 0f; + private int swipeThreshold; + private int swipeVelocityThreshold; private int mDocumentOrientationDegrees; private int mDocumentState; private String mEncryptedDocumentPassword; @@ -435,6 +436,8 @@ public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) } }); + initializeGestures(); + GestureHelper.attach(PdfViewer.this, binding.webview, new GestureHelper.GestureListener() { @Override @@ -463,10 +466,6 @@ public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2, float float absDeltaX = Math.abs(deltaX); float absDeltaY = Math.abs(deltaY); - float screenDensity = getResources().getDisplayMetrics().density; - int swipeThreshold = (int) (SWIPE_THRESHOLD * screenDensity); - int swipeVelocityThreshold = (int) (SWIPE_VELOCITY_THRESHOLD * screenDensity); - // Check primarily horizontal if (absDeltaX > absDeltaY && absDeltaX > swipeThreshold && @@ -561,6 +560,12 @@ public void onZoomEnd() { } } + private void initializeGestures() { + ViewConfiguration vc = ViewConfiguration.get(this); + swipeThreshold = vc.getScaledTouchSlop() * 4; + swipeVelocityThreshold = vc.getScaledMinimumFlingVelocity(); + } + private void purgeWebView() { binding.webview.removeJavascriptInterface("channel"); binding.getRoot().removeView(binding.webview);