The only 16KB page size compatible PDF viewer for Android 15+. Built with Android's native PdfRenderer API β zero native libraries, guaranteed Google Play compatibility.
- β
Zoom Control:
enableZoom(boolean)to disable pinch-to-zoom (#5) - β
Builder Chain Fix:
setMinZoom(),setMidZoom(),setMaxZoom()now returnPDFView(#5) - β
7 New Listeners:
onDraw(),onDrawAll(),onPageScroll(),onRender(),onTap(),onLongPress(),onPageError() - β
Default Scroll Handle: Built-in
DefaultScrollHandlewith draggable handle + page indicator (#6) - β
Continuous Scroll Fix:
loadFromFile()now respectscontinuousScrollMode(#7) - β
Zoom Gap Fix: Per-page
canvas.scale()eliminates visual gaps during pinch zoom (#2)
Fully backward compatible β just update the version:
implementation 'com.github.alamin5g:Alamin5G-PDF-Viewer:v1.0.17'Starting November 1st, 2025, Google Play REQUIRES all apps targeting Android 15+ to support 16KB page size alignment. Most PDF libraries (including barteksc/AndroidPdfViewer) use native .so files that are NOT 16KB compatible.
| Feature | Alamin5G PDF Viewer | barteksc/AndroidPdfViewer | MuPDF |
|---|---|---|---|
| 16KB Compatible | β YES | β NO (native libs) | β NO |
| Google Play 2025 | β Passes | β Rejected | β Rejected |
| Native Libraries | β Zero | β Multiple .so files | β .so files |
| File Size | β ~200 KB | ||
| Android 15+ | β Full support | β Crashes | β Issues |
- PDF Rendering: High-quality rendering with customizable quality (ARGB_8888 / RGB_565)
- Multiple Sources: Assets, files, URIs, bytes, streams, and remote URLs
- Zoom Support: Pinch-to-zoom, double-tap zoom, programmatic zoom, enable/disable toggle
- Navigation: Swipe gestures, page jumping with animation, continuous scroll
- Fit Policies: Width, height, or both
- Night Mode: Inverted colors for dark theme
- Scroll Handle: Built-in
DefaultScrollHandlewith page indicator - 7 Listener Callbacks: Draw, render, scroll, tap, long press, page error
- Memory Efficient: LRU caching, configurable cache size, lazy loading
- 16KB Compatible: Uses native
PdfRendererβ zero.sofiles
Add JitPack to your settings.gradle:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}dependencies {
implementation 'com.github.alamin5g:Alamin5G-PDF-Viewer:v1.0.17'
}If your app includes native libraries, ensure your build.gradle has:
android {
defaultConfig {
ndk { version "28.0.0" }
}
packagingOptions {
jniLibs { useLegacyPackaging = false }
}
}Note: Alamin5G PDF Viewer itself has NO native libraries β this config is only needed if your app uses other native libs.
<com.alamin5g.pdf.PDFView
android:id="@+id/pdfView"
android:layout_width="match_parent"
android:layout_height="match_parent" />PDFView pdfView = findViewById(R.id.pdfView);
pdfView.fromAsset("sample.pdf")
.enableSwipe(true)
.enableDoubletap(true)
.defaultPage(0)
.onLoad(nbPages -> Log.d("PDF", "Loaded " + nbPages + " pages"))
.onPageChange((page, pageCount) -> Log.d("PDF", "Page " + (page + 1) + "/" + pageCount))
.onError(error -> Log.e("PDF", "Error: " + error.getMessage()))
.load(); // β¬
οΈ Must be called LAST
β οΈ Important: Always call.load()last in the chain, after setting all callbacks. See Callback Setup below.
public class PdfActivity extends AppCompatActivity {
private PDFView pdfView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pdf);
pdfView = findViewById(R.id.pdfView);
pdfView.fromAsset("document.pdf")
.enableSwipe(true)
.enableDoubletap(true)
.fitPolicy(PDFView.FitPolicy.WIDTH)
.onLoad(nbPages -> Toast.makeText(this, nbPages + " pages", Toast.LENGTH_SHORT).show())
.onError(error -> Toast.makeText(this, "Error: " + error.getMessage(), Toast.LENGTH_LONG).show())
.load();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (pdfView != null) pdfView.recycle();
}
}Callbacks must be registered before .load() is called:
// β
Correct β load() called AFTER callbacks
pdfView.fromAsset("file.pdf")
.onLoad(nbPages -> { /* fires */ })
.onError(error -> { /* fires */ })
.load();
// β Wrong β load() called BEFORE callbacks
pdfView.fromAsset("file.pdf").load();
pdfView.onLoad(nbPages -> { /* NEVER fires */ });// From assets
pdfView.fromAsset("sample.pdf").load();
// From file
pdfView.fromFile(new File("/path/to/document.pdf")).load();
// From URI
pdfView.fromUri(uri).load();
// From byte array
pdfView.fromBytes(pdfBytes).load();
// From input stream
pdfView.fromStream(inputStream).load();
// From URL (auto-calls load after download)
pdfView.fromUrl("https://example.com/document.pdf")
.onDownloadProgress((bytes, total, progress) -> {
Log.d("PDF", "Download: " + progress + "%");
})
.onLoad(nbPages -> { /* loaded */ })
.onError(error -> { /* handle error */ });Add to AndroidManifest.xml for URL loading:
<uses-permission android:name="android.permission.INTERNET" />pdfView.fromAsset("sample.pdf")
// Navigation
.enableSwipe(true) // Enable swipe navigation
.swipeHorizontal(false) // false = vertical, true = horizontal
.enableDoubletap(true) // Enable double-tap zoom
.defaultPage(0) // Starting page (0-based)
.pages(0, 2, 1, 3) // Custom page order
// Display
.enableAntialiasing(true) // Smooth rendering
.setNightMode(false) // Night mode (inverted colors)
.useBestQuality(true) // ARGB_8888 vs RGB_565
.fitPolicy(PDFView.FitPolicy.WIDTH) // WIDTH, HEIGHT, or BOTH
.spacing(10) // Spacing between pages in dp
.autoSpacing(false) // Dynamic spacing to fit pages
.pageFitPolicy(PDFView.FitPolicy.WIDTH) // Per-page fit policy
.fitEachPage(false) // Fit each page individually
.enableAnnotationRendering(true) // Render annotations
// Zoom
.enableZoom(true) // Enable pinch-to-zoom
.setMinZoom(0.5f) // Minimum zoom level
.setMidZoom(1.75f) // Middle zoom for double-tap
.setMaxZoom(5.0f) // Maximum zoom level
// Scroll Handle
.scrollHandle(new DefaultScrollHandle(this)) // Built-in scroll handle
// Performance
.setCacheSize(10) // Pages to cache (default: 10)
// Callbacks
.onLoad(listener)
.onPageChange(listener)
.onError(listener)
.onDownloadProgress(listener)
.onDraw(listener)
.onDrawAll(listener)
.onPageScroll(listener)
.onRender(listener)
.onTap(listener)
.onLongPress(listener)
.onPageError(listener)
.load();.fitPolicy(PDFView.FitPolicy.WIDTH) // Fit to width (recommended)
.fitPolicy(PDFView.FitPolicy.HEIGHT) // Fit to height
.fitPolicy(PDFView.FitPolicy.BOTH) // Fit both dimensions| Method | Description |
|---|---|
fromAsset(String) |
Load from assets folder |
fromFile(File) |
Load from file |
fromUri(Uri) |
Load from URI |
fromBytes(byte[]) |
Load from byte array |
fromStream(InputStream) |
Load from input stream |
fromUrl(String) |
Load from URL with download progress |
load() |
Must be called last! Starts loading |
| Method | Description |
|---|---|
enableSwipe(boolean) |
Enable/disable swipe navigation |
swipeHorizontal(boolean) |
Set swipe direction |
defaultPage(int) |
Set starting page (0-based) |
pages(int...) |
Load specific pages in custom order |
jumpTo(int) |
Jump to page |
jumpTo(int, boolean) |
Jump to page with optional animation |
| Method | Description |
|---|---|
enableAntialiasing(boolean) |
Smooth rendering |
setNightMode(boolean) |
Invert colors for night reading |
useBestQuality(boolean) |
ARGB_8888 vs RGB_565 |
fitPolicy(FitPolicy) |
WIDTH, HEIGHT, or BOTH |
spacing(int) |
Spacing between pages in dp |
autoSpacing(boolean) |
Dynamic spacing |
pageFitPolicy(FitPolicy) |
Per-page fit policy |
fitEachPage(boolean) |
Fit each page individually |
enableAnnotationRendering(boolean) |
Render annotations |
scrollHandle(View) |
Custom or default scroll handle |
| Method | Description |
|---|---|
enableZoom(boolean) |
Enable/disable pinch-to-zoom |
enableDoubletap(boolean) |
Enable double-tap to zoom |
setMinZoom(float) |
Minimum zoom level |
setMidZoom(float) |
Middle zoom for double-tap |
setMaxZoom(float) |
Maximum zoom level |
zoomTo(float) |
Set zoom level |
zoomWithAnimation(float) |
Zoom with animation |
resetZoom() |
Reset to default zoom |
resetZoomWithAnimation() |
Smooth reset animation |
| Method | Description |
|---|---|
onLoad(OnLoadCompleteListener) |
PDF loading complete |
onPageChange(OnPageChangeListener) |
Page changed |
onError(OnErrorListener) |
Error occurred |
onDownloadProgress(OnDownloadProgressListener) |
URL download progress |
onDraw(OnDrawListener) |
After each page draw |
onDrawAll(OnDrawAllListener) |
After all pages drawn |
onPageScroll(OnPageScrollListener) |
During scroll with offset |
onRender(OnRenderListener) |
After page renders |
onTap(OnTapListener) |
Single tap (return bool to consume) |
onLongPress(OnLongPressListener) |
Long press gesture |
onPageError(OnPageErrorListener) |
Page-specific render error |
// Navigation
pdfView.jumpTo(5); // Jump to page 6 (0-based)
pdfView.jumpTo(5, true); // With animation
int page = pdfView.getCurrentPage();
int total = pdfView.getPageCount();
// Position
float offset = pdfView.getPositionOffset(); // 0.0 = top, 1.0 = bottom
pdfView.setPositionOffset(0.5f); // Scroll to 50%
pdfView.moveTo(100f, 200f); // Absolute position
pdfView.moveRelativeTo(50f, -100f); // Relative movement
// Zoom
pdfView.zoomTo(2.0f);
pdfView.zoomWithAnimation(2.0f);
pdfView.zoomWithAnimation(centerX, centerY, 2.0f);
pdfView.resetZoom();
pdfView.resetZoomWithAnimation();
float zoom = pdfView.getZoom();
// Scroll configuration
pdfView.pageFling(true); // Jump to page on fling
pdfView.pageSnap(true); // Snap to page boundaries
pdfView.stopFling(); // Stop ongoing fling
// State checks
boolean zoomed = pdfView.isZooming();
boolean closed = pdfView.isRecycled();
boolean zoomEnabled = pdfView.isZoomEnabled();
// Page info
android.util.SizeF size = pdfView.getPageSize(pageIndex);
int page = pdfView.getPageAtPositionOffset(0.5f);
pdfView.performPageSnap();
// Cleanup
pdfView.recycle();The built-in DefaultScrollHandle provides a draggable scroll indicator with page number bubble:
import com.alamin5g.pdf.scroll.DefaultScrollHandle;
// Vertical handle (right edge, default)
pdfView.fromAsset("file.pdf")
.scrollHandle(new DefaultScrollHandle(this))
.load();
// Horizontal handle (bottom edge)
pdfView.fromAsset("file.pdf")
.scrollHandle(new DefaultScrollHandle(this, true))
.load();
// Custom scroll handle (your own View)
pdfView.fromAsset("file.pdf")
.scrollHandle(myCustomView)
.load();
// Remove scroll handle
pdfView.scrollHandle(null);DefaultScrollHandle features:
- π Draggable handle bar synced with scroll position
- π’ Page indicator bubble showing current page number
- β±οΈ Auto-hides indicator after 1.5 seconds
βοΈ Vertical (right edge) and horizontal (bottom edge) orientations- π No feedback loops between handle drag and PDF scroll
pdfView.setNightMode(true);pdfView.fromAsset("sample.pdf")
.pages(0, 2, 1, 3) // Load pages in custom order
.load();pdfView.password("your-pdf-password");pdfView.setCacheSize(15); // Cache 15 pages (default: 10)Supports HTTP/HTTPS URLs, Google Drive, Dropbox, AWS S3, and any web server serving PDF files.
// Google Drive β use direct download link
String url = "https://drive.google.com/uc?export=download&id=FILE_ID";
pdfView.fromUrl(url).load();
// Dropbox β replace www.dropbox.com with dl.dropboxusercontent.com
String url = "https://dl.dropboxusercontent.com/s/abc123/document.pdf";
pdfView.fromUrl(url).load();
// AWS S3
String url = "https://bucket.s3.amazonaws.com/path/to/document.pdf";
pdfView.fromUrl(url).load();PDF not loading from assets:
// Ensure file is in src/main/assets/ folder
pdfView.fromAsset("folder/sample.pdf").load();Memory issues with large PDFs:
pdfView.setCacheSize(5).useBestQuality(false).load();Zoom not working:
pdfView.enableZoom(true).enableDoubletap(true).load();- Android API: 24+ (Android 7.0)
- Target SDK: 34+ (for 16KB compatibility)
- Java: 8+ (11+ recommended)
- Permissions: None for local files;
INTERNETfor URL loading - ProGuard: No additional rules needed
enableZoom(boolean), builder chain fix (#5)- 7 new listener interfaces:
OnDraw,OnDrawAll,OnPageScroll,OnRender,OnTap,OnLongPress,OnPageError DefaultScrollHandlewith draggable handle + page indicator (#6)loadFromFile()respectscontinuousScrollMode(#7)- Per-page
canvas.scale()eliminates zoom gaps (#2) canScrollVertically()zoom fix,getMinZoom(),getMidZoom(),isZoomEnabled()
- Callbacks fixed:
onLoad(),onError(),onDownloadProgress()(#4) - Smart cache replacement, progressive rendering, 3-page buffer
onPageChanged()fires during scroll, URI loading 20x faster
- 40+ new methods, 97% feature parity with AndroidPdfViewer
- Swipe gesture fix, jumpTo fix, position/movement methods
- Memory reduced from ~2 GB to ~35 MB for large PDFs
- Lazy loading, LRU eviction, virtual scrolling
- Auto re-render at zoom resolution, no pixelation
- Zoom anchoring fix, PDF centering,
resetZoomWithAnimation()
fromUrl(), download progress, cloud integration
- Recycled bitmap crash fix,
setCacheSize()added
- Annotation rendering, scroll handle, page spacing, individual page fitting
- 16KB compatibility, core PDF features, LRU caching, gestures
Why it matters: Google Play requires 16KB page size support for Android 15+ apps starting November 2025. Libraries with native .so files (barteksc/AndroidPdfViewer, MuPDF, pdf.js wrappers) will cause app rejection.
Alamin5G PDF Viewer:
- β
Uses Android's native
PdfRendererAPI β no.sofiles - β ~200 KB library size (vs 20+ MB for native alternatives)
- β Works on 4KB and 16KB devices
- β Compatible with Android 15, 16, and beyond
Migration from barteksc/AndroidPdfViewer:
// OLD (not 16KB compatible)
// implementation 'com.github.barteksc:android-pdf-viewer:3.2.0-beta.1'
// NEW (16KB compatible)
implementation 'com.github.alamin5g:Alamin5G-PDF-Viewer:1.0.17'// Just change the import:
// OLD: import com.github.barteksc.pdfviewer.PDFView;
// NEW: import com.alamin5g.pdf.PDFView;MIT License
Copyright (c) 2025 Alamin5G
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Contributions are welcome! Please feel free to submit a Pull Request.
- GitHub Issues: Report bugs or request features
- JitPack: Library status and builds
Made with β€οΈ by Alamin5G