├── .gitignore
├── .idea
├── caches
│ └── build_file_checksums.ser
├── codeStyles
│ └── Project.xml
├── gradle.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── android-pdf-viewer
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── github
│ │ └── pdfviewer
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── github
│ │ │ └── pdfviewer
│ │ │ ├── IShowPage.java
│ │ │ ├── PDFAdapter.java
│ │ │ ├── PDFConfig.java
│ │ │ ├── PDFView.java
│ │ │ ├── PDFViewActivity.java
│ │ │ ├── PDFViewPager.java
│ │ │ └── PDFZoomImageView.java
│ └── res
│ │ ├── layout
│ │ ├── activity_pdf.xml
│ │ └── each_page.xml
│ │ └── values
│ │ ├── attrs.xml
│ │ └── strings.xml
│ └── test
│ └── java
│ └── com
│ └── github
│ └── pdfviewer
│ └── ExampleUnitTest.java
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── pdfrenderer
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ ├── sample.pdf
│ │ └── sample101.pdf
│ ├── java
│ │ └── com
│ │ │ └── pdfrenderer
│ │ │ ├── PdfLoad.java
│ │ │ └── RetrofitInterface.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── pdfrenderer
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── screenshot_01.png
├── screenshot_02.png
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/libraries
5 | /.idea/modules.xml
6 | /.idea/workspace.xml
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 |
--------------------------------------------------------------------------------
/.idea/caches/build_file_checksums.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/.idea/caches/build_file_checksums.ser
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AndroidPdfViewer
2 | Lightweight PDF Renderer Android
3 |
4 | Library for displaying PDF documents on Android, with animations, gestures, zoom and double tap support.Works on API 21 (Android 5.0) and higher.
5 |
6 | [](https://www.jitpack.io/#manishkummar21/AndroidPdfViewer)
7 |
8 |
9 | # Installation
10 | Step 1. Add it in your root build.gradle at the end of repositories:
11 | ```
12 | allprojects {
13 | repositories {
14 | ...
15 | maven { url 'https://www.jitpack.io' }
16 | }
17 | }
18 | ```
19 | Step 2. Add the dependency
20 | ```
21 | dependencies {
22 | implementation 'com.github.manishkummar21:AndroidPdfViewer:1.0'
23 | }
24 | ```
25 |
26 | # Include PDFViewActivity in your manifest file
27 |
28 | ```
29 |
30 |
31 | ```
32 |
33 | # Load a PDF file from anywhere in your code
34 |
35 | ```
36 | PDFView.with(context)
37 | .fromfilepath(fileapth)
38 | .swipeHorizontal(false) //if false pageswipe is vertical otherwise its horizontal
39 | .start();
40 | ```
41 |
42 |
43 | //Example of loading pdf file from asset folder
44 | ```
45 |
46 | File file = new File(getCacheDir(), "sample.pdf");
47 | if (!file.exists()) {
48 |
49 | try {
50 | InputStream asset = getAssets().open("sample.pdf");
51 | FileOutputStream output = null;
52 | output = new FileOutputStream(file);
53 | final byte[] buffer = new byte[1024];
54 | int size;
55 | while ((size = asset.read(buffer)) != -1) {
56 | output.write(buffer, 0, size);
57 | }
58 | asset.close();
59 | output.close();
60 | } catch (IOException e) {
61 | e.printStackTrace();
62 | }
63 |
64 | }
65 |
66 |
67 | PDFView.with(this)
68 | .fromfilepath(file.getAbsolutePath()))
69 | .swipeHorizontal(false)
70 | .start()
71 | ```
72 | 
73 |
74 | 
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/android-pdf-viewer/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/android-pdf-viewer/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 |
4 | group='com.github.manishkummar21'
5 |
6 |
7 | android {
8 | compileSdkVersion 28
9 |
10 |
11 |
12 | defaultConfig {
13 | minSdkVersion 21
14 | targetSdkVersion 28
15 | versionCode 1
16 | versionName "1.1"
17 |
18 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
19 |
20 | }
21 |
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 |
29 | }
30 |
31 | dependencies {
32 | implementation fileTree(dir: 'libs', include: ['*.jar'])
33 |
34 | implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
35 | testImplementation 'junit:junit:4.12'
36 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
37 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
38 | }
39 |
--------------------------------------------------------------------------------
/android-pdf-viewer/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/android-pdf-viewer/src/androidTest/java/com/github/pdfviewer/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.github.pdfviewer;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.github.pdfviewer.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/android-pdf-viewer/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/android-pdf-viewer/src/main/java/com/github/pdfviewer/IShowPage.java:
--------------------------------------------------------------------------------
1 | package com.github.pdfviewer;
2 |
3 | import android.graphics.Bitmap;
4 |
5 | public interface IShowPage {
6 |
7 | public Bitmap showPage(int index);
8 | }
9 |
--------------------------------------------------------------------------------
/android-pdf-viewer/src/main/java/com/github/pdfviewer/PDFAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.pdfviewer;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.support.v4.view.PagerAdapter;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.LinearLayout;
10 |
11 | public class PDFAdapter extends PagerAdapter {
12 |
13 | private int page_count;
14 | private Context context;
15 | private IShowPage listener;
16 |
17 |
18 | public PDFAdapter(Context context, IShowPage listener, int page_count) {
19 | this.context = context;
20 | this.listener = listener;
21 | this.page_count = page_count;
22 | }
23 |
24 | @Override
25 | public int getCount() {
26 | return page_count;
27 | }
28 |
29 | @NonNull
30 | @Override
31 | public Object instantiateItem(@NonNull ViewGroup container, int position) {
32 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
33 |
34 | View itemView = inflater.inflate(R.layout.each_page, container, false);
35 |
36 | PDFZoomImageView imageView = (PDFZoomImageView ) itemView.findViewById(R.id.image);
37 |
38 | imageView.setImageBitmap(listener.showPage(position));
39 |
40 | container.addView(itemView);
41 |
42 | return itemView;
43 | }
44 |
45 | public void destroyItem(ViewGroup container, int position, Object object) {
46 | container.removeView((LinearLayout) object);
47 | }
48 |
49 | public boolean isViewFromObject(View view, Object object) {
50 | return view == object;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/android-pdf-viewer/src/main/java/com/github/pdfviewer/PDFConfig.java:
--------------------------------------------------------------------------------
1 | package com.github.pdfviewer;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | public class PDFConfig implements Parcelable {
7 |
8 | public static final String EXTRA_CONFIG = "PDFConfig";
9 |
10 | private String filepath;
11 | private int swipeorientation;
12 | // private String network_url;
13 |
14 | public PDFConfig() {
15 |
16 | }
17 |
18 | protected PDFConfig(Parcel in) {
19 | this.filepath = in.readString();
20 | this.swipeorientation = in.readInt();
21 | // this.network_url = in.readString();
22 | }
23 |
24 |
25 | public String getFilepath() {
26 | return filepath;
27 | }
28 |
29 | public void setFilepath(String filepath) {
30 | this.filepath = filepath;
31 | }
32 |
33 | public int getSwipeorientation() {
34 | return swipeorientation;
35 | }
36 |
37 | public void setSwipeorientation(int swipeorientation) {
38 | this.swipeorientation = swipeorientation;
39 | }
40 |
41 | // public String getNetwork_url() {
42 | // return network_url;
43 | // }
44 | //
45 | // public void setNetwork_url(String network_url) {
46 | // this.network_url = network_url;
47 | // }
48 |
49 | @Override
50 | public void writeToParcel(Parcel dest, int flags) {
51 | dest.writeString(filepath);
52 | dest.writeInt(swipeorientation);
53 | // dest.writeString(network_url);
54 |
55 | }
56 |
57 | @Override
58 | public int describeContents() {
59 | return 0;
60 | }
61 |
62 | public static final Creator CREATOR = new Creator() {
63 | @Override
64 | public PDFConfig createFromParcel(Parcel in) {
65 | return new PDFConfig(in);
66 | }
67 |
68 | @Override
69 | public PDFConfig[] newArray(int size) {
70 | return new PDFConfig[size];
71 | }
72 | };
73 | }
74 |
--------------------------------------------------------------------------------
/android-pdf-viewer/src/main/java/com/github/pdfviewer/PDFView.java:
--------------------------------------------------------------------------------
1 | package com.github.pdfviewer;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.support.v4.app.Fragment;
7 |
8 | public class PDFView {
9 |
10 | public static abstract class BaseBuilder {
11 |
12 | protected PDFConfig config;
13 |
14 | public BaseBuilder(Context context) {
15 | this.config = new PDFConfig();
16 | }
17 | }
18 |
19 | public static abstract class Builder extends BaseBuilder {
20 |
21 | public Builder(Activity activity) {
22 | super(activity);
23 | }
24 |
25 | public Builder(Fragment fragment) {
26 | super(fragment.getActivity());
27 | }
28 |
29 | public Builder fromfilepath(String filepath) {
30 | config.setFilepath(filepath);
31 | return this;
32 | }
33 |
34 |
35 | public Builder swipeHorizontal(boolean swipeOrientation) {
36 | config.setSwipeorientation(swipeOrientation ? 0 : 1);
37 | return this;
38 | }
39 |
40 | // public Builder fromurl(String url) {
41 | // config.setNetwork_url(url);
42 | // return this;
43 | // }
44 |
45 | public abstract void start();
46 |
47 | }
48 |
49 | static class ActivityBuilder extends Builder {
50 | private Activity activity;
51 |
52 | public ActivityBuilder(Activity activity) {
53 | super(activity);
54 | this.activity = activity;
55 | }
56 |
57 | @Override
58 | public void start() {
59 | Intent intent = new Intent(activity, PDFViewActivity.class);
60 | intent.putExtra(PDFConfig.EXTRA_CONFIG, config);
61 | activity.startActivity(intent);
62 | }
63 | }
64 |
65 | public static Builder with(Activity activity) {
66 | return new ActivityBuilder(activity);
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/android-pdf-viewer/src/main/java/com/github/pdfviewer/PDFViewActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.pdfviewer;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.graphics.Bitmap;
6 | import android.graphics.pdf.PdfRenderer;
7 | import android.os.Bundle;
8 | import android.os.ParcelFileDescriptor;
9 | import android.support.annotation.Nullable;
10 | import android.support.v7.app.AppCompatActivity;
11 | import android.widget.Toast;
12 |
13 | import java.io.File;
14 | import java.io.FileNotFoundException;
15 | import java.io.IOException;
16 |
17 | public class PDFViewActivity extends AppCompatActivity implements IShowPage {
18 |
19 | private PDFViewPager pdfviewpager;
20 | private PDFAdapter adapter;
21 |
22 | private static final String STATE_CURRENT_PAGE_INDEX = "current_page_index";
23 | private ParcelFileDescriptor mFileDescriptor;
24 | private PdfRenderer mPdfRenderer;
25 | private PdfRenderer.Page mCurrentPage;
26 | private int mPageIndex;
27 | private PDFConfig config;
28 |
29 |
30 | @Override
31 | protected void onCreate(@Nullable Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | Intent intent = getIntent();
34 | if (intent == null) {
35 | finish();
36 | return;
37 | }
38 |
39 | config = intent.getParcelableExtra(PDFConfig.EXTRA_CONFIG);
40 |
41 | setContentView(R.layout.activity_pdf);
42 | pdfviewpager = (PDFViewPager) findViewById(R.id.pdfviewfpager);
43 | pdfviewpager.setSwipeOrientation(config.getSwipeorientation());
44 |
45 | mPageIndex = 0;
46 | // If there is a savedInstanceState (screen orientations, etc.), we restore the page index.
47 | if (null != savedInstanceState) {
48 | mPageIndex = savedInstanceState.getInt(STATE_CURRENT_PAGE_INDEX, 0);
49 | }
50 |
51 |
52 | //render the pdf view
53 | try {
54 | openRenderer(this);
55 | setUpViewPager();
56 | } catch (FileNotFoundException e) {
57 | Toast.makeText(this, "Error! " + e.getMessage(), Toast.LENGTH_SHORT).show();
58 | finish();
59 | } catch (IOException e) {
60 | e.printStackTrace();
61 | Toast.makeText(this, "Error! " + e.getMessage(), Toast.LENGTH_SHORT).show();
62 | finish();
63 | }
64 | }
65 |
66 | @Override
67 | public void onStart() {
68 | super.onStart();
69 |
70 | }
71 |
72 | private void setUpViewPager() {
73 | adapter = new PDFAdapter(PDFViewActivity.this, this, mPdfRenderer.getPageCount());
74 | pdfviewpager.setAdapter(adapter);
75 | pdfviewpager.setCurrentItem(mPageIndex);
76 | }
77 |
78 | @Override
79 | protected void onDestroy() {
80 | try {
81 | closeRenderer();
82 | } catch (IOException e) {
83 | e.printStackTrace();
84 | }
85 | super.onDestroy();
86 | }
87 |
88 | @Override
89 | public void onSaveInstanceState(Bundle outState) {
90 | super.onSaveInstanceState(outState);
91 | if (null != mCurrentPage) {
92 | outState.putInt(STATE_CURRENT_PAGE_INDEX, mCurrentPage.getIndex());
93 | }
94 | }
95 |
96 |
97 | private void openRenderer(Context context) throws IOException {
98 | File file = new File(config.getFilepath());
99 | mFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
100 | if (mFileDescriptor != null) {
101 | mPdfRenderer = new PdfRenderer(mFileDescriptor);
102 | }
103 |
104 | }
105 |
106 |
107 | private void closeRenderer() throws IOException {
108 | if (null != mCurrentPage)
109 | mCurrentPage.close();
110 |
111 | if (null != mPdfRenderer)
112 | mPdfRenderer.close();
113 |
114 | if (null != mFileDescriptor)
115 | mFileDescriptor.close();
116 | }
117 |
118 | public Bitmap showPage(int index) {
119 | if (mPdfRenderer.getPageCount() <= index) {
120 | return null;
121 | }
122 | // Make sure to close the current page before opening another one.
123 | if (null != mCurrentPage) {
124 | mCurrentPage.close();
125 | }
126 | // Use `openPage` to open a specific page in PDF.
127 | mCurrentPage = mPdfRenderer.openPage(index);
128 | // Important: the destination bitmap must be ARGB (not RGB).
129 | Bitmap bitmap = Bitmap.createBitmap(mCurrentPage.getWidth(), mCurrentPage.getHeight(),
130 | Bitmap.Config.ARGB_8888);
131 | mCurrentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
132 | return bitmap;
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/android-pdf-viewer/src/main/java/com/github/pdfviewer/PDFViewPager.java:
--------------------------------------------------------------------------------
1 | package com.github.pdfviewer;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.support.v4.view.ViewPager;
6 | import android.util.AttributeSet;
7 | import android.view.MotionEvent;
8 | import android.view.View;
9 |
10 | public class PDFViewPager extends ViewPager {
11 | public static final int HORIZONTAL = 0;
12 | public static final int VERTICAL = 1;
13 |
14 | private int mSwipeOrientation;
15 | // private ScrollerCustomDuration mScroller = null;
16 |
17 | public PDFViewPager(Context context) {
18 | super(context);
19 | mSwipeOrientation = HORIZONTAL;
20 | }
21 |
22 | public PDFViewPager(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | setSwipeOrientation(context, attrs);
25 | }
26 |
27 | @Override
28 | public boolean onTouchEvent(MotionEvent event) {
29 | return super.onTouchEvent(mSwipeOrientation == VERTICAL ? swapXY(event) : event);
30 | }
31 |
32 | @Override
33 | public boolean onInterceptTouchEvent(MotionEvent event) {
34 | if (mSwipeOrientation == VERTICAL) {
35 | boolean intercepted = super.onInterceptTouchEvent(swapXY(event));
36 | swapXY(event);
37 | return intercepted;
38 | }
39 | return super.onInterceptTouchEvent(event);
40 | }
41 |
42 | public void setSwipeOrientation(int swipeOrientation) {
43 | if (swipeOrientation == HORIZONTAL || swipeOrientation == VERTICAL)
44 | mSwipeOrientation = swipeOrientation;
45 | else
46 | throw new IllegalStateException("Swipe Orientation can be either CustomViewPager.HORIZONTAL" +
47 | " or CustomViewPager.VERTICAL");
48 | initSwipeMethods();
49 | }
50 |
51 | private void setSwipeOrientation(Context context, AttributeSet attrs) {
52 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PDFViewPager);
53 | mSwipeOrientation = typedArray.getInteger(R.styleable.PDFViewPager_swipe_orientation, 0);
54 | typedArray.recycle();
55 | initSwipeMethods();
56 | }
57 |
58 | private void initSwipeMethods() {
59 | if (mSwipeOrientation == VERTICAL) {
60 | // The majority of the work is done over here
61 | setPageTransformer(true, new VerticalPageTransformer());
62 | // The easiest way to get rid of the overscroll drawing that happens on the left and right
63 | setOverScrollMode(OVER_SCROLL_NEVER);
64 | }
65 | }
66 |
67 | /**
68 | * Set the factor by which the duration will change
69 | */
70 | // public void setScrollDurationFactor(double scrollFactor) {
71 | // mScroller.setScrollDurationFactor(scrollFactor);
72 | // }
73 |
74 | private MotionEvent swapXY(MotionEvent event) {
75 | float width = getWidth();
76 | float height = getHeight();
77 |
78 | float newX = (event.getY() / height) * width;
79 | float newY = (event.getX() / width) * height;
80 |
81 | event.setLocation(newX, newY);
82 | return event;
83 | }
84 |
85 | private class VerticalPageTransformer implements PageTransformer {
86 |
87 | @Override
88 | public void transformPage(View page, float position) {
89 | if (position < -1) {
90 | // This page is way off-screen to the left
91 | page.setAlpha(0);
92 | } else if (position <= 1) {
93 | page.setAlpha(1);
94 |
95 | // Counteract the default slide transition
96 | page.setTranslationX(page.getWidth() * -position);
97 |
98 | // set Y position to swipe in from top
99 | float yPosition = position * page.getHeight();
100 | page.setTranslationY(yPosition);
101 | } else {
102 | // This page is way off screen to the right
103 | page.setAlpha(0);
104 | }
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/android-pdf-viewer/src/main/java/com/github/pdfviewer/PDFZoomImageView.java:
--------------------------------------------------------------------------------
1 | package com.github.pdfviewer;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.Configuration;
6 | import android.graphics.Bitmap;
7 | import android.graphics.Canvas;
8 | import android.graphics.Matrix;
9 | import android.graphics.PointF;
10 | import android.graphics.RectF;
11 | import android.graphics.drawable.Drawable;
12 | import android.net.Uri;
13 | import android.os.Build.VERSION;
14 | import android.os.Build.VERSION_CODES;
15 | import android.os.Bundle;
16 | import android.os.Parcelable;
17 | import android.util.AttributeSet;
18 | import android.util.Log;
19 | import android.view.GestureDetector;
20 | import android.view.MotionEvent;
21 | import android.view.ScaleGestureDetector;
22 | import android.view.View;
23 | import android.view.animation.AccelerateDecelerateInterpolator;
24 | import android.widget.ImageView;
25 | import android.widget.OverScroller;
26 | import android.widget.Scroller;
27 |
28 | public class PDFZoomImageView extends ImageView {
29 |
30 | private static final String DEBUG = "DEBUG";
31 |
32 | //
33 | // SuperMin and SuperMax multipliers. Determine how much the image can be
34 | // zoomed below or above the zoom boundaries, before animating back to the
35 | // min/max zoom boundary.
36 | //
37 | private static final float SUPER_MIN_MULTIPLIER = .75f;
38 | private static final float SUPER_MAX_MULTIPLIER = 1.25f;
39 |
40 | //
41 | // Scale of image ranges from minScale to maxScale, where minScale == 1
42 | // when the image is stretched to fit view.
43 | //
44 | private float normalizedScale;
45 |
46 | //
47 | // Matrix applied to image. MSCALE_X and MSCALE_Y should always be equal.
48 | // MTRANS_X and MTRANS_Y are the other values used. prevMatrix is the matrix
49 | // saved prior to the screen rotating.
50 | //
51 | private Matrix matrix, prevMatrix;
52 |
53 | private static enum State { NONE, DRAG, ZOOM, FLING, ANIMATE_ZOOM };
54 | private State state;
55 |
56 | private float minScale;
57 | private float maxScale;
58 | private float superMinScale;
59 | private float superMaxScale;
60 | private float[] m;
61 |
62 | private Context context;
63 | private Fling fling;
64 |
65 | private ScaleType mScaleType;
66 |
67 | private boolean imageRenderedAtLeastOnce;
68 | private boolean onDrawReady;
69 |
70 | private ZoomVariables delayedZoomVariables;
71 |
72 | //
73 | // Size of view and previous view size (ie before rotation)
74 | //
75 | private int viewWidth, viewHeight, prevViewWidth, prevViewHeight;
76 |
77 | //
78 | // Size of image when it is stretched to fit view. Before and After rotation.
79 | //
80 | private float matchViewWidth, matchViewHeight, prevMatchViewWidth, prevMatchViewHeight;
81 |
82 | private ScaleGestureDetector mScaleDetector;
83 | private GestureDetector mGestureDetector;
84 | private GestureDetector.OnDoubleTapListener doubleTapListener = null;
85 | private OnTouchListener userTouchListener = null;
86 | private OnTouchImageViewListener touchImageViewListener = null;
87 |
88 | public PDFZoomImageView(Context context) {
89 | super(context);
90 | sharedConstructing(context);
91 | }
92 |
93 | public PDFZoomImageView(Context context, AttributeSet attrs) {
94 | super(context, attrs);
95 | sharedConstructing(context);
96 | }
97 |
98 | public PDFZoomImageView(Context context, AttributeSet attrs, int defStyle) {
99 | super(context, attrs, defStyle);
100 | sharedConstructing(context);
101 | }
102 |
103 | private void sharedConstructing(Context context) {
104 | super.setClickable(true);
105 | this.context = context;
106 | mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
107 | mGestureDetector = new GestureDetector(context, new GestureListener());
108 | matrix = new Matrix();
109 | prevMatrix = new Matrix();
110 | m = new float[9];
111 | normalizedScale = 1;
112 | if (mScaleType == null) {
113 | mScaleType = ScaleType.FIT_CENTER;
114 | }
115 | minScale = 1;
116 | maxScale = 3;
117 | superMinScale = SUPER_MIN_MULTIPLIER * minScale;
118 | superMaxScale = SUPER_MAX_MULTIPLIER * maxScale;
119 | setImageMatrix(matrix);
120 | setScaleType(ScaleType.MATRIX);
121 | setState(State.NONE);
122 | onDrawReady = false;
123 | super.setOnTouchListener(new PrivateOnTouchListener());
124 | }
125 |
126 | @Override
127 | public void setOnTouchListener(OnTouchListener l) {
128 | userTouchListener = l;
129 | }
130 |
131 | public void setOnTouchImageViewListener(OnTouchImageViewListener l) {
132 | touchImageViewListener = l;
133 | }
134 |
135 | public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener l) {
136 | doubleTapListener = l;
137 | }
138 |
139 | @Override
140 | public void setImageResource(int resId) {
141 | super.setImageResource(resId);
142 | savePreviousImageValues();
143 | fitImageToView();
144 | }
145 |
146 | @Override
147 | public void setImageBitmap(Bitmap bm) {
148 | super.setImageBitmap(bm);
149 | savePreviousImageValues();
150 | fitImageToView();
151 | }
152 |
153 | @Override
154 | public void setImageDrawable(Drawable drawable) {
155 | super.setImageDrawable(drawable);
156 | savePreviousImageValues();
157 | fitImageToView();
158 | }
159 |
160 | @Override
161 | public void setImageURI(Uri uri) {
162 | super.setImageURI(uri);
163 | savePreviousImageValues();
164 | fitImageToView();
165 | }
166 |
167 | @Override
168 | public void setScaleType(ScaleType type) {
169 | if (type == ScaleType.FIT_START || type == ScaleType.FIT_END) {
170 | throw new UnsupportedOperationException("TouchImageView does not support FIT_START or FIT_END");
171 | }
172 | if (type == ScaleType.MATRIX) {
173 | super.setScaleType(ScaleType.MATRIX);
174 |
175 | } else {
176 | mScaleType = type;
177 | if (onDrawReady) {
178 | //
179 | // If the image is already rendered, scaleType has been called programmatically
180 | // and the TouchImageView should be updated with the new scaleType.
181 | //
182 | setZoom(this);
183 | }
184 | }
185 | }
186 |
187 | @Override
188 | public ScaleType getScaleType() {
189 | return mScaleType;
190 | }
191 |
192 | /**
193 | * Returns false if image is in initial, unzoomed state. False, otherwise.
194 | * @return true if image is zoomed
195 | */
196 | public boolean isZoomed() {
197 | return normalizedScale != 1;
198 | }
199 |
200 | /**
201 | * Return a Rect representing the zoomed image.
202 | * @return rect representing zoomed image
203 | */
204 | public RectF getZoomedRect() {
205 | if (mScaleType == ScaleType.FIT_XY) {
206 | throw new UnsupportedOperationException("getZoomedRect() not supported with FIT_XY");
207 | }
208 | PointF topLeft = transformCoordTouchToBitmap(0, 0, true);
209 | PointF bottomRight = transformCoordTouchToBitmap(viewWidth, viewHeight, true);
210 |
211 | float w = getDrawable().getIntrinsicWidth();
212 | float h = getDrawable().getIntrinsicHeight();
213 | return new RectF(topLeft.x / w, topLeft.y / h, bottomRight.x / w, bottomRight.y / h);
214 | }
215 |
216 | /**
217 | * Save the current matrix and view dimensions
218 | * in the prevMatrix and prevView variables.
219 | */
220 | private void savePreviousImageValues() {
221 | if (matrix != null && viewHeight != 0 && viewWidth != 0) {
222 | matrix.getValues(m);
223 | prevMatrix.setValues(m);
224 | prevMatchViewHeight = matchViewHeight;
225 | prevMatchViewWidth = matchViewWidth;
226 | prevViewHeight = viewHeight;
227 | prevViewWidth = viewWidth;
228 | }
229 | }
230 |
231 | @Override
232 | public Parcelable onSaveInstanceState() {
233 | Bundle bundle = new Bundle();
234 | bundle.putParcelable("instanceState", super.onSaveInstanceState());
235 | bundle.putFloat("saveScale", normalizedScale);
236 | bundle.putFloat("matchViewHeight", matchViewHeight);
237 | bundle.putFloat("matchViewWidth", matchViewWidth);
238 | bundle.putInt("viewWidth", viewWidth);
239 | bundle.putInt("viewHeight", viewHeight);
240 | matrix.getValues(m);
241 | bundle.putFloatArray("matrix", m);
242 | bundle.putBoolean("imageRendered", imageRenderedAtLeastOnce);
243 | return bundle;
244 | }
245 |
246 | @Override
247 | public void onRestoreInstanceState(Parcelable state) {
248 | if (state instanceof Bundle) {
249 | Bundle bundle = (Bundle) state;
250 | normalizedScale = bundle.getFloat("saveScale");
251 | m = bundle.getFloatArray("matrix");
252 | prevMatrix.setValues(m);
253 | prevMatchViewHeight = bundle.getFloat("matchViewHeight");
254 | prevMatchViewWidth = bundle.getFloat("matchViewWidth");
255 | prevViewHeight = bundle.getInt("viewHeight");
256 | prevViewWidth = bundle.getInt("viewWidth");
257 | imageRenderedAtLeastOnce = bundle.getBoolean("imageRendered");
258 | super.onRestoreInstanceState(bundle.getParcelable("instanceState"));
259 | return;
260 | }
261 |
262 | super.onRestoreInstanceState(state);
263 | }
264 |
265 | @Override
266 | protected void onDraw(Canvas canvas) {
267 | onDrawReady = true;
268 | imageRenderedAtLeastOnce = true;
269 | if (delayedZoomVariables != null) {
270 | setZoom(delayedZoomVariables.scale, delayedZoomVariables.focusX, delayedZoomVariables.focusY, delayedZoomVariables.scaleType);
271 | delayedZoomVariables = null;
272 | }
273 | super.onDraw(canvas);
274 | }
275 |
276 | @Override
277 | public void onConfigurationChanged(Configuration newConfig) {
278 | super.onConfigurationChanged(newConfig);
279 | savePreviousImageValues();
280 | }
281 |
282 | /**
283 | * Get the max zoom multiplier.
284 | * @return max zoom multiplier.
285 | */
286 | public float getMaxZoom() {
287 | return maxScale;
288 | }
289 |
290 | /**
291 | * Set the max zoom multiplier. Default value: 3.
292 | * @param max max zoom multiplier.
293 | */
294 | public void setMaxZoom(float max) {
295 | maxScale = max;
296 | superMaxScale = SUPER_MAX_MULTIPLIER * maxScale;
297 | }
298 |
299 | /**
300 | * Get the min zoom multiplier.
301 | * @return min zoom multiplier.
302 | */
303 | public float getMinZoom() {
304 | return minScale;
305 | }
306 |
307 | /**
308 | * Get the current zoom. This is the zoom relative to the initial
309 | * scale, not the original resource.
310 | * @return current zoom multiplier.
311 | */
312 | public float getCurrentZoom() {
313 | return normalizedScale;
314 | }
315 |
316 | /**
317 | * Set the min zoom multiplier. Default value: 1.
318 | * @param min min zoom multiplier.
319 | */
320 | public void setMinZoom(float min) {
321 | minScale = min;
322 | superMinScale = SUPER_MIN_MULTIPLIER * minScale;
323 | }
324 |
325 | /**
326 | * Reset zoom and translation to initial state.
327 | */
328 | public void resetZoom() {
329 | normalizedScale = 1;
330 | fitImageToView();
331 | }
332 |
333 | /**
334 | * Set zoom to the specified scale. Image will be centered by default.
335 | * @param scale
336 | */
337 | public void setZoom(float scale) {
338 | setZoom(scale, 0.5f, 0.5f);
339 | }
340 |
341 | /**
342 | * Set zoom to the specified scale. Image will be centered around the point
343 | * (focusX, focusY). These floats range from 0 to 1 and denote the focus point
344 | * as a fraction from the left and top of the view. For example, the top left
345 | * corner of the image would be (0, 0). And the bottom right corner would be (1, 1).
346 | * @param scale
347 | * @param focusX
348 | * @param focusY
349 | */
350 | public void setZoom(float scale, float focusX, float focusY) {
351 | setZoom(scale, focusX, focusY, mScaleType);
352 | }
353 |
354 | /**
355 | * Set zoom to the specified scale. Image will be centered around the point
356 | * (focusX, focusY). These floats range from 0 to 1 and denote the focus point
357 | * as a fraction from the left and top of the view. For example, the top left
358 | * corner of the image would be (0, 0). And the bottom right corner would be (1, 1).
359 | * @param scale
360 | * @param focusX
361 | * @param focusY
362 | * @param scaleType
363 | */
364 | public void setZoom(float scale, float focusX, float focusY, ScaleType scaleType) {
365 | //
366 | // setZoom can be called before the image is on the screen, but at this point,
367 | // image and view sizes have not yet been calculated in onMeasure. Thus, we should
368 | // delay calling setZoom until the view has been measured.
369 | //
370 | if (!onDrawReady) {
371 | delayedZoomVariables = new ZoomVariables(scale, focusX, focusY, scaleType);
372 | return;
373 | }
374 |
375 | if (scaleType != mScaleType) {
376 | setScaleType(scaleType);
377 | }
378 | resetZoom();
379 | scaleImage(scale, viewWidth / 2, viewHeight / 2, true);
380 | matrix.getValues(m);
381 | m[Matrix.MTRANS_X] = -((focusX * getImageWidth()) - (viewWidth * 0.5f));
382 | m[Matrix.MTRANS_Y] = -((focusY * getImageHeight()) - (viewHeight * 0.5f));
383 | matrix.setValues(m);
384 | fixTrans();
385 | setImageMatrix(matrix);
386 | }
387 |
388 |
389 | public void setZoom(PDFZoomImageView img) {
390 | PointF center = img.getScrollPosition();
391 | setZoom(img.getCurrentZoom(), center.x, center.y, img.getScaleType());
392 | }
393 |
394 | /**
395 | * Return the point at the center of the zoomed image. The PointF coordinates range
396 | * in value between 0 and 1 and the focus point is denoted as a fraction from the left
397 | * and top of the view. For example, the top left corner of the image would be (0, 0).
398 | * And the bottom right corner would be (1, 1).
399 | * @return PointF representing the scroll position of the zoomed image.
400 | */
401 | public PointF getScrollPosition() {
402 | Drawable drawable = getDrawable();
403 | if (drawable == null) {
404 | return null;
405 | }
406 | int drawableWidth = drawable.getIntrinsicWidth();
407 | int drawableHeight = drawable.getIntrinsicHeight();
408 |
409 | PointF point = transformCoordTouchToBitmap(viewWidth / 2, viewHeight / 2, true);
410 | point.x /= drawableWidth;
411 | point.y /= drawableHeight;
412 | return point;
413 | }
414 |
415 | /**
416 | * Set the focus point of the zoomed image. The focus points are denoted as a fraction from the
417 | * left and top of the view. The focus points can range in value between 0 and 1.
418 | * @param focusX
419 | * @param focusY
420 | */
421 | public void setScrollPosition(float focusX, float focusY) {
422 | setZoom(normalizedScale, focusX, focusY);
423 | }
424 |
425 | /**
426 | * Performs boundary checking and fixes the image matrix if it
427 | * is out of bounds.
428 | */
429 | private void fixTrans() {
430 | matrix.getValues(m);
431 | float transX = m[Matrix.MTRANS_X];
432 | float transY = m[Matrix.MTRANS_Y];
433 |
434 | float fixTransX = getFixTrans(transX, viewWidth, getImageWidth());
435 | float fixTransY = getFixTrans(transY, viewHeight, getImageHeight());
436 |
437 | if (fixTransX != 0 || fixTransY != 0) {
438 | matrix.postTranslate(fixTransX, fixTransY);
439 | }
440 | }
441 |
442 | /**
443 | * When transitioning from zooming from focus to zoom from center (or vice versa)
444 | * the image can become unaligned within the view. This is apparent when zooming
445 | * quickly. When the content size is less than the view size, the content will often
446 | * be centered incorrectly within the view. fixScaleTrans first calls fixTrans() and
447 | * then makes sure the image is centered correctly within the view.
448 | */
449 | private void fixScaleTrans() {
450 | fixTrans();
451 | matrix.getValues(m);
452 | if (getImageWidth() < viewWidth) {
453 | m[Matrix.MTRANS_X] = (viewWidth - getImageWidth()) / 2;
454 | }
455 |
456 | if (getImageHeight() < viewHeight) {
457 | m[Matrix.MTRANS_Y] = (viewHeight - getImageHeight()) / 2;
458 | }
459 | matrix.setValues(m);
460 | }
461 |
462 | private float getFixTrans(float trans, float viewSize, float contentSize) {
463 | float minTrans, maxTrans;
464 |
465 | if (contentSize <= viewSize) {
466 | minTrans = 0;
467 | maxTrans = viewSize - contentSize;
468 |
469 | } else {
470 | minTrans = viewSize - contentSize;
471 | maxTrans = 0;
472 | }
473 |
474 | if (trans < minTrans)
475 | return -trans + minTrans;
476 | if (trans > maxTrans)
477 | return -trans + maxTrans;
478 | return 0;
479 | }
480 |
481 | private float getFixDragTrans(float delta, float viewSize, float contentSize) {
482 | if (contentSize <= viewSize) {
483 | return 0;
484 | }
485 | return delta;
486 | }
487 |
488 | private float getImageWidth() {
489 | return matchViewWidth * normalizedScale;
490 | }
491 |
492 | private float getImageHeight() {
493 | return matchViewHeight * normalizedScale;
494 | }
495 |
496 | @Override
497 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
498 | Drawable drawable = getDrawable();
499 | if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) {
500 | setMeasuredDimension(0, 0);
501 | return;
502 | }
503 |
504 | int drawableWidth = drawable.getIntrinsicWidth();
505 | int drawableHeight = drawable.getIntrinsicHeight();
506 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
507 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
508 | int heightSize = MeasureSpec.getSize(heightMeasureSpec);
509 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
510 | viewWidth = setViewSize(widthMode, widthSize, drawableWidth);
511 | viewHeight = setViewSize(heightMode, heightSize, drawableHeight);
512 |
513 | //
514 | // Set view dimensions
515 | //
516 | setMeasuredDimension(viewWidth, viewHeight);
517 |
518 | //
519 | // Fit content within view
520 | //
521 | fitImageToView();
522 | }
523 |
524 | /**
525 | * If the normalizedScale is equal to 1, then the image is made to fit the screen. Otherwise,
526 | * it is made to fit the screen according to the dimensions of the previous image matrix. This
527 | * allows the image to maintain its zoom after rotation.
528 | */
529 | private void fitImageToView() {
530 | Drawable drawable = getDrawable();
531 | if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) {
532 | return;
533 | }
534 | if (matrix == null || prevMatrix == null) {
535 | return;
536 | }
537 |
538 | int drawableWidth = drawable.getIntrinsicWidth();
539 | int drawableHeight = drawable.getIntrinsicHeight();
540 |
541 | //
542 | // Scale image for view
543 | //
544 | float scaleX = (float) viewWidth / drawableWidth;
545 | float scaleY = (float) viewHeight / drawableHeight;
546 |
547 | switch (mScaleType) {
548 | case CENTER:
549 | scaleX = scaleY = 1;
550 | break;
551 |
552 | case CENTER_CROP:
553 | scaleX = scaleY = Math.max(scaleX, scaleY);
554 | break;
555 |
556 | case CENTER_INSIDE:
557 | scaleX = scaleY = Math.min(1, Math.min(scaleX, scaleY));
558 |
559 | case FIT_CENTER:
560 | scaleX = scaleY = Math.min(scaleX, scaleY);
561 | break;
562 |
563 | case FIT_XY:
564 | break;
565 |
566 | default:
567 | //
568 | // FIT_START and FIT_END not supported
569 | //
570 | throw new UnsupportedOperationException("TouchImageView does not support FIT_START or FIT_END");
571 |
572 | }
573 |
574 | //
575 | // Center the image
576 | //
577 | float redundantXSpace = viewWidth - (scaleX * drawableWidth);
578 | float redundantYSpace = viewHeight - (scaleY * drawableHeight);
579 | matchViewWidth = viewWidth - redundantXSpace;
580 | matchViewHeight = viewHeight - redundantYSpace;
581 | if (!isZoomed() && !imageRenderedAtLeastOnce) {
582 | //
583 | // Stretch and center image to fit view
584 | //
585 | matrix.setScale(scaleX, scaleY);
586 | matrix.postTranslate(redundantXSpace / 2, redundantYSpace / 2);
587 | normalizedScale = 1;
588 |
589 | } else {
590 | //
591 | // These values should never be 0 or we will set viewWidth and viewHeight
592 | // to NaN in translateMatrixAfterRotate. To avoid this, call savePreviousImageValues
593 | // to set them equal to the current values.
594 | //
595 | if (prevMatchViewWidth == 0 || prevMatchViewHeight == 0) {
596 | savePreviousImageValues();
597 | }
598 |
599 | prevMatrix.getValues(m);
600 |
601 | //
602 | // Rescale Matrix after rotation
603 | //
604 | m[Matrix.MSCALE_X] = matchViewWidth / drawableWidth * normalizedScale;
605 | m[Matrix.MSCALE_Y] = matchViewHeight / drawableHeight * normalizedScale;
606 |
607 | //
608 | // TransX and TransY from previous matrix
609 | //
610 | float transX = m[Matrix.MTRANS_X];
611 | float transY = m[Matrix.MTRANS_Y];
612 |
613 | //
614 | // Width
615 | //
616 | float prevActualWidth = prevMatchViewWidth * normalizedScale;
617 | float actualWidth = getImageWidth();
618 | translateMatrixAfterRotate(Matrix.MTRANS_X, transX, prevActualWidth, actualWidth, prevViewWidth, viewWidth, drawableWidth);
619 |
620 | //
621 | // Height
622 | //
623 | float prevActualHeight = prevMatchViewHeight * normalizedScale;
624 | float actualHeight = getImageHeight();
625 | translateMatrixAfterRotate(Matrix.MTRANS_Y, transY, prevActualHeight, actualHeight, prevViewHeight, viewHeight, drawableHeight);
626 |
627 | //
628 | // Set the matrix to the adjusted scale and translate values.
629 | //
630 | matrix.setValues(m);
631 | }
632 | fixTrans();
633 | setImageMatrix(matrix);
634 | }
635 |
636 | /**
637 | * Set view dimensions based on layout params
638 | *
639 | * @param mode
640 | * @param size
641 | * @param drawableWidth
642 | * @return
643 | */
644 | private int setViewSize(int mode, int size, int drawableWidth) {
645 | int viewSize;
646 | switch (mode) {
647 | case MeasureSpec.EXACTLY:
648 | viewSize = size;
649 | break;
650 |
651 | case MeasureSpec.AT_MOST:
652 | viewSize = Math.min(drawableWidth, size);
653 | break;
654 |
655 | case MeasureSpec.UNSPECIFIED:
656 | viewSize = drawableWidth;
657 | break;
658 |
659 | default:
660 | viewSize = size;
661 | break;
662 | }
663 | return viewSize;
664 | }
665 |
666 | /**
667 | * After rotating, the matrix needs to be translated. This function finds the area of image
668 | * which was previously centered and adjusts translations so that is again the center, post-rotation.
669 | *
670 | * @param axis Matrix.MTRANS_X or Matrix.MTRANS_Y
671 | * @param trans the value of trans in that axis before the rotation
672 | * @param prevImageSize the width/height of the image before the rotation
673 | * @param imageSize width/height of the image after rotation
674 | * @param prevViewSize width/height of view before rotation
675 | * @param viewSize width/height of view after rotation
676 | * @param drawableSize width/height of drawable
677 | */
678 | private void translateMatrixAfterRotate(int axis, float trans, float prevImageSize, float imageSize, int prevViewSize, int viewSize, int drawableSize) {
679 | if (imageSize < viewSize) {
680 | //
681 | // The width/height of image is less than the view's width/height. Center it.
682 | //
683 | m[axis] = (viewSize - (drawableSize * m[Matrix.MSCALE_X])) * 0.5f;
684 |
685 | } else if (trans > 0) {
686 | //
687 | // The image is larger than the view, but was not before rotation. Center it.
688 | //
689 | m[axis] = -((imageSize - viewSize) * 0.5f);
690 |
691 | } else {
692 | //
693 | // Find the area of the image which was previously centered in the view. Determine its distance
694 | // from the left/top side of the view as a fraction of the entire image's width/height. Use that percentage
695 | // to calculate the trans in the new view width/height.
696 | //
697 | float percentage = (Math.abs(trans) + (0.5f * prevViewSize)) / prevImageSize;
698 | m[axis] = -((percentage * imageSize) - (viewSize * 0.5f));
699 | }
700 | }
701 |
702 | private void setState(State state) {
703 | this.state = state;
704 | }
705 |
706 | public boolean canScrollHorizontallyFroyo(int direction) {
707 | return canScrollHorizontally(direction);
708 | }
709 |
710 | @Override
711 | public boolean canScrollHorizontally(int direction) {
712 | matrix.getValues(m);
713 | float x = m[Matrix.MTRANS_X];
714 |
715 | if (getImageWidth() < viewWidth) {
716 | return false;
717 |
718 | } else if (x >= -1 && direction < 0) {
719 | return false;
720 |
721 | } else if (Math.abs(x) + viewWidth + 1 >= getImageWidth() && direction > 0) {
722 | return false;
723 | }
724 |
725 | return true;
726 | }
727 |
728 | /**
729 | * Gesture Listener detects a single click or long click and passes that on
730 | * to the view's listener.
731 | * @author Ortiz
732 | *
733 | */
734 | private class GestureListener extends GestureDetector.SimpleOnGestureListener {
735 |
736 | @Override
737 | public boolean onSingleTapConfirmed(MotionEvent e)
738 | {
739 | if(doubleTapListener != null) {
740 | return doubleTapListener.onSingleTapConfirmed(e);
741 | }
742 | return performClick();
743 | }
744 |
745 | @Override
746 | public void onLongPress(MotionEvent e)
747 | {
748 | performLongClick();
749 | }
750 |
751 | @Override
752 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
753 | {
754 | if (fling != null) {
755 | //
756 | // If a previous fling is still active, it should be cancelled so that two flings
757 | // are not run simultaenously.
758 | //
759 | fling.cancelFling();
760 | }
761 | fling = new Fling((int) velocityX, (int) velocityY);
762 | compatPostOnAnimation(fling);
763 | return super.onFling(e1, e2, velocityX, velocityY);
764 | }
765 |
766 | @Override
767 | public boolean onDoubleTap(MotionEvent e) {
768 | boolean consumed = false;
769 | if(doubleTapListener != null) {
770 | consumed = doubleTapListener.onDoubleTap(e);
771 | }
772 | if (state == State.NONE) {
773 | float targetZoom = (normalizedScale == minScale) ? maxScale : minScale;
774 | DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom, e.getX(), e.getY(), false);
775 | compatPostOnAnimation(doubleTap);
776 | consumed = true;
777 | }
778 | return consumed;
779 | }
780 |
781 | @Override
782 | public boolean onDoubleTapEvent(MotionEvent e) {
783 | if(doubleTapListener != null) {
784 | return doubleTapListener.onDoubleTapEvent(e);
785 | }
786 | return false;
787 | }
788 | }
789 |
790 | public interface OnTouchImageViewListener {
791 | public void onMove();
792 | }
793 |
794 | /**
795 | * Responsible for all touch events. Handles the heavy lifting of drag and also sends
796 | * touch events to Scale Detector and Gesture Detector.
797 | * @author Ortiz
798 | *
799 | */
800 | private class PrivateOnTouchListener implements OnTouchListener {
801 |
802 | //
803 | // Remember last point position for dragging
804 | //
805 | private PointF last = new PointF();
806 |
807 | @Override
808 | public boolean onTouch(View v, MotionEvent event) {
809 | mScaleDetector.onTouchEvent(event);
810 | mGestureDetector.onTouchEvent(event);
811 | PointF curr = new PointF(event.getX(), event.getY());
812 |
813 | if (state == State.NONE || state == State.DRAG || state == State.FLING) {
814 | switch (event.getAction()) {
815 | case MotionEvent.ACTION_DOWN:
816 | last.set(curr);
817 | if (fling != null)
818 | fling.cancelFling();
819 | setState(State.DRAG);
820 | break;
821 |
822 | case MotionEvent.ACTION_MOVE:
823 | if (state == State.DRAG) {
824 | float deltaX = curr.x - last.x;
825 | float deltaY = curr.y - last.y;
826 | float fixTransX = getFixDragTrans(deltaX, viewWidth, getImageWidth());
827 | float fixTransY = getFixDragTrans(deltaY, viewHeight, getImageHeight());
828 | matrix.postTranslate(fixTransX, fixTransY);
829 | fixTrans();
830 | last.set(curr.x, curr.y);
831 | }
832 | break;
833 |
834 | case MotionEvent.ACTION_UP:
835 | case MotionEvent.ACTION_POINTER_UP:
836 | setState(State.NONE);
837 | break;
838 | }
839 | }
840 |
841 | setImageMatrix(matrix);
842 |
843 | //
844 | // User-defined OnTouchListener
845 | //
846 | if(userTouchListener != null) {
847 | userTouchListener.onTouch(v, event);
848 | }
849 |
850 | //
851 | // OnTouchImageViewListener is set: TouchImageView dragged by user.
852 | //
853 | if (touchImageViewListener != null) {
854 | touchImageViewListener.onMove();
855 | }
856 |
857 | //
858 | // indicate event was handled
859 | //
860 | return true;
861 | }
862 | }
863 |
864 | /**
865 | * ScaleListener detects user two finger scaling and scales image.
866 | * @author Ortiz
867 | *
868 | */
869 | private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
870 | @Override
871 | public boolean onScaleBegin(ScaleGestureDetector detector) {
872 | setState(State.ZOOM);
873 | return true;
874 | }
875 |
876 | @Override
877 | public boolean onScale(ScaleGestureDetector detector) {
878 | scaleImage(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY(), true);
879 |
880 | //
881 | // OnTouchImageViewListener is set: TouchImageView pinch zoomed by user.
882 | //
883 | if (touchImageViewListener != null) {
884 | touchImageViewListener.onMove();
885 | }
886 | return true;
887 | }
888 |
889 | @Override
890 | public void onScaleEnd(ScaleGestureDetector detector) {
891 | super.onScaleEnd(detector);
892 | setState(State.NONE);
893 | boolean animateToZoomBoundary = false;
894 | float targetZoom = normalizedScale;
895 | if (normalizedScale > maxScale) {
896 | targetZoom = maxScale;
897 | animateToZoomBoundary = true;
898 |
899 | } else if (normalizedScale < minScale) {
900 | targetZoom = minScale;
901 | animateToZoomBoundary = true;
902 | }
903 |
904 | if (animateToZoomBoundary) {
905 | DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom, viewWidth / 2, viewHeight / 2, true);
906 | compatPostOnAnimation(doubleTap);
907 | }
908 | }
909 | }
910 |
911 | private void scaleImage(double deltaScale, float focusX, float focusY, boolean stretchImageToSuper) {
912 |
913 | float lowerScale, upperScale;
914 | if (stretchImageToSuper) {
915 | lowerScale = superMinScale;
916 | upperScale = superMaxScale;
917 |
918 | } else {
919 | lowerScale = minScale;
920 | upperScale = maxScale;
921 | }
922 |
923 | float origScale = normalizedScale;
924 | normalizedScale *= deltaScale;
925 | if (normalizedScale > upperScale) {
926 | normalizedScale = upperScale;
927 | deltaScale = upperScale / origScale;
928 | } else if (normalizedScale < lowerScale) {
929 | normalizedScale = lowerScale;
930 | deltaScale = lowerScale / origScale;
931 | }
932 |
933 | matrix.postScale((float) deltaScale, (float) deltaScale, focusX, focusY);
934 | fixScaleTrans();
935 | }
936 |
937 | /**
938 | * DoubleTapZoom calls a series of runnables which apply
939 | * an animated zoom in/out graphic to the image.
940 | * @author Ortiz
941 | *
942 | */
943 | private class DoubleTapZoom implements Runnable {
944 |
945 | private long startTime;
946 | private static final float ZOOM_TIME = 500;
947 | private float startZoom, targetZoom;
948 | private float bitmapX, bitmapY;
949 | private boolean stretchImageToSuper;
950 | private AccelerateDecelerateInterpolator interpolator = new AccelerateDecelerateInterpolator();
951 | private PointF startTouch;
952 | private PointF endTouch;
953 |
954 | DoubleTapZoom(float targetZoom, float focusX, float focusY, boolean stretchImageToSuper) {
955 | setState(State.ANIMATE_ZOOM);
956 | startTime = System.currentTimeMillis();
957 | this.startZoom = normalizedScale;
958 | this.targetZoom = targetZoom;
959 | this.stretchImageToSuper = stretchImageToSuper;
960 | PointF bitmapPoint = transformCoordTouchToBitmap(focusX, focusY, false);
961 | this.bitmapX = bitmapPoint.x;
962 | this.bitmapY = bitmapPoint.y;
963 |
964 | //
965 | // Used for translating image during scaling
966 | //
967 | startTouch = transformCoordBitmapToTouch(bitmapX, bitmapY);
968 | endTouch = new PointF(viewWidth / 2, viewHeight / 2);
969 | }
970 |
971 | @Override
972 | public void run() {
973 | float t = interpolate();
974 | double deltaScale = calculateDeltaScale(t);
975 | scaleImage(deltaScale, bitmapX, bitmapY, stretchImageToSuper);
976 | translateImageToCenterTouchPosition(t);
977 | fixScaleTrans();
978 | setImageMatrix(matrix);
979 |
980 | //
981 | // OnTouchImageViewListener is set: double tap runnable updates listener
982 | // with every frame.
983 | //
984 | if (touchImageViewListener != null) {
985 | touchImageViewListener.onMove();
986 | }
987 |
988 | if (t < 1f) {
989 | //
990 | // We haven't finished zooming
991 | //
992 | compatPostOnAnimation(this);
993 |
994 | } else {
995 | //
996 | // Finished zooming
997 | //
998 | setState(State.NONE);
999 | }
1000 | }
1001 |
1002 | /**
1003 | * Interpolate between where the image should start and end in order to translate
1004 | * the image so that the point that is touched is what ends up centered at the end
1005 | * of the zoom.
1006 | * @param t
1007 | */
1008 | private void translateImageToCenterTouchPosition(float t) {
1009 | float targetX = startTouch.x + t * (endTouch.x - startTouch.x);
1010 | float targetY = startTouch.y + t * (endTouch.y - startTouch.y);
1011 | PointF curr = transformCoordBitmapToTouch(bitmapX, bitmapY);
1012 | matrix.postTranslate(targetX - curr.x, targetY - curr.y);
1013 | }
1014 |
1015 | /**
1016 | * Use interpolator to get t
1017 | * @return
1018 | */
1019 | private float interpolate() {
1020 | long currTime = System.currentTimeMillis();
1021 | float elapsed = (currTime - startTime) / ZOOM_TIME;
1022 | elapsed = Math.min(1f, elapsed);
1023 | return interpolator.getInterpolation(elapsed);
1024 | }
1025 |
1026 | /**
1027 | * Interpolate the current targeted zoom and get the delta
1028 | * from the current zoom.
1029 | * @param t
1030 | * @return
1031 | */
1032 | private double calculateDeltaScale(float t) {
1033 | double zoom = startZoom + t * (targetZoom - startZoom);
1034 | return zoom / normalizedScale;
1035 | }
1036 | }
1037 |
1038 | /**
1039 | * This function will transform the coordinates in the touch event to the coordinate
1040 | * system of the drawable that the imageview contain
1041 | * @param x x-coordinate of touch event
1042 | * @param y y-coordinate of touch event
1043 | * @param clipToBitmap Touch event may occur within view, but outside image content. True, to clip return value
1044 | * to the bounds of the bitmap size.
1045 | * @return Coordinates of the point touched, in the coordinate system of the original drawable.
1046 | */
1047 | private PointF transformCoordTouchToBitmap(float x, float y, boolean clipToBitmap) {
1048 | matrix.getValues(m);
1049 | float origW = getDrawable().getIntrinsicWidth();
1050 | float origH = getDrawable().getIntrinsicHeight();
1051 | float transX = m[Matrix.MTRANS_X];
1052 | float transY = m[Matrix.MTRANS_Y];
1053 | float finalX = ((x - transX) * origW) / getImageWidth();
1054 | float finalY = ((y - transY) * origH) / getImageHeight();
1055 |
1056 | if (clipToBitmap) {
1057 | finalX = Math.min(Math.max(finalX, 0), origW);
1058 | finalY = Math.min(Math.max(finalY, 0), origH);
1059 | }
1060 |
1061 | return new PointF(finalX , finalY);
1062 | }
1063 |
1064 | /**
1065 | * Inverse of transformCoordTouchToBitmap. This function will transform the coordinates in the
1066 | * drawable's coordinate system to the view's coordinate system.
1067 | * @param bx x-coordinate in original bitmap coordinate system
1068 | * @param by y-coordinate in original bitmap coordinate system
1069 | * @return Coordinates of the point in the view's coordinate system.
1070 | */
1071 | private PointF transformCoordBitmapToTouch(float bx, float by) {
1072 | matrix.getValues(m);
1073 | float origW = getDrawable().getIntrinsicWidth();
1074 | float origH = getDrawable().getIntrinsicHeight();
1075 | float px = bx / origW;
1076 | float py = by / origH;
1077 | float finalX = m[Matrix.MTRANS_X] + getImageWidth() * px;
1078 | float finalY = m[Matrix.MTRANS_Y] + getImageHeight() * py;
1079 | return new PointF(finalX , finalY);
1080 | }
1081 |
1082 | /**
1083 | * Fling launches sequential runnables which apply
1084 | * the fling graphic to the image. The values for the translation
1085 | * are interpolated by the Scroller.
1086 | * @author Ortiz
1087 | *
1088 | */
1089 | private class Fling implements Runnable {
1090 |
1091 | CompatScroller scroller;
1092 | int currX, currY;
1093 |
1094 | Fling(int velocityX, int velocityY) {
1095 | setState(State.FLING);
1096 | scroller = new CompatScroller(context);
1097 | matrix.getValues(m);
1098 |
1099 | int startX = (int) m[Matrix.MTRANS_X];
1100 | int startY = (int) m[Matrix.MTRANS_Y];
1101 | int minX, maxX, minY, maxY;
1102 |
1103 | if (getImageWidth() > viewWidth) {
1104 | minX = viewWidth - (int) getImageWidth();
1105 | maxX = 0;
1106 |
1107 | } else {
1108 | minX = maxX = startX;
1109 | }
1110 |
1111 | if (getImageHeight() > viewHeight) {
1112 | minY = viewHeight - (int) getImageHeight();
1113 | maxY = 0;
1114 |
1115 | } else {
1116 | minY = maxY = startY;
1117 | }
1118 |
1119 | scroller.fling(startX, startY, (int) velocityX, (int) velocityY, minX,
1120 | maxX, minY, maxY);
1121 | currX = startX;
1122 | currY = startY;
1123 | }
1124 |
1125 | public void cancelFling() {
1126 | if (scroller != null) {
1127 | setState(State.NONE);
1128 | scroller.forceFinished(true);
1129 | }
1130 | }
1131 |
1132 | @Override
1133 | public void run() {
1134 |
1135 | //
1136 | // OnTouchImageViewListener is set: TouchImageView listener has been flung by user.
1137 | // Listener runnable updated with each frame of fling animation.
1138 | //
1139 | if (touchImageViewListener != null) {
1140 | touchImageViewListener.onMove();
1141 | }
1142 |
1143 | if (scroller.isFinished()) {
1144 | scroller = null;
1145 | return;
1146 | }
1147 |
1148 | if (scroller.computeScrollOffset()) {
1149 | int newX = scroller.getCurrX();
1150 | int newY = scroller.getCurrY();
1151 | int transX = newX - currX;
1152 | int transY = newY - currY;
1153 | currX = newX;
1154 | currY = newY;
1155 | matrix.postTranslate(transX, transY);
1156 | fixTrans();
1157 | setImageMatrix(matrix);
1158 | compatPostOnAnimation(this);
1159 | }
1160 | }
1161 | }
1162 |
1163 | @TargetApi(VERSION_CODES.GINGERBREAD)
1164 | private class CompatScroller {
1165 | Scroller scroller;
1166 | OverScroller overScroller;
1167 | boolean isPreGingerbread;
1168 |
1169 | public CompatScroller(Context context) {
1170 | if (VERSION.SDK_INT < VERSION_CODES.GINGERBREAD) {
1171 | isPreGingerbread = true;
1172 | scroller = new Scroller(context);
1173 |
1174 | } else {
1175 | isPreGingerbread = false;
1176 | overScroller = new OverScroller(context);
1177 | }
1178 | }
1179 |
1180 | public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) {
1181 | if (isPreGingerbread) {
1182 | scroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
1183 | } else {
1184 | overScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
1185 | }
1186 | }
1187 |
1188 | public void forceFinished(boolean finished) {
1189 | if (isPreGingerbread) {
1190 | scroller.forceFinished(finished);
1191 | } else {
1192 | overScroller.forceFinished(finished);
1193 | }
1194 | }
1195 |
1196 | public boolean isFinished() {
1197 | if (isPreGingerbread) {
1198 | return scroller.isFinished();
1199 | } else {
1200 | return overScroller.isFinished();
1201 | }
1202 | }
1203 |
1204 | public boolean computeScrollOffset() {
1205 | if (isPreGingerbread) {
1206 | return scroller.computeScrollOffset();
1207 | } else {
1208 | overScroller.computeScrollOffset();
1209 | return overScroller.computeScrollOffset();
1210 | }
1211 | }
1212 |
1213 | public int getCurrX() {
1214 | if (isPreGingerbread) {
1215 | return scroller.getCurrX();
1216 | } else {
1217 | return overScroller.getCurrX();
1218 | }
1219 | }
1220 |
1221 | public int getCurrY() {
1222 | if (isPreGingerbread) {
1223 | return scroller.getCurrY();
1224 | } else {
1225 | return overScroller.getCurrY();
1226 | }
1227 | }
1228 | }
1229 |
1230 | @TargetApi(VERSION_CODES.JELLY_BEAN)
1231 | private void compatPostOnAnimation(Runnable runnable) {
1232 | if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
1233 | postOnAnimation(runnable);
1234 |
1235 | } else {
1236 | postDelayed(runnable, 1000/60);
1237 | }
1238 | }
1239 |
1240 | private class ZoomVariables {
1241 | public float scale;
1242 | public float focusX;
1243 | public float focusY;
1244 | public ScaleType scaleType;
1245 |
1246 | public ZoomVariables(float scale, float focusX, float focusY, ScaleType scaleType) {
1247 | this.scale = scale;
1248 | this.focusX = focusX;
1249 | this.focusY = focusY;
1250 | this.scaleType = scaleType;
1251 | }
1252 | }
1253 |
1254 | private void printMatrixInfo() {
1255 | float[] n = new float[9];
1256 | matrix.getValues(n);
1257 | Log.d(DEBUG, "Scale: " + n[Matrix.MSCALE_X] + " TransX: " + n[Matrix.MTRANS_X] + " TransY: " + n[Matrix.MTRANS_Y]);
1258 | }
1259 | }
--------------------------------------------------------------------------------
/android-pdf-viewer/src/main/res/layout/activity_pdf.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/android-pdf-viewer/src/main/res/layout/each_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/android-pdf-viewer/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/android-pdf-viewer/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | android-pdf-viewer
3 |
4 |
--------------------------------------------------------------------------------
/android-pdf-viewer/src/test/java/com/github/pdfviewer/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.github.pdfviewer;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.pdfrenderer"
7 | minSdkVersion 21
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.1"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(include: ['*.jar'], dir: 'libs')
23 | implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
24 | implementation 'com.android.support.constraint:constraint-layout:1.1.2'
25 | testImplementation 'junit:junit:4.12'
26 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
28 | implementation 'com.squareup.retrofit2:retrofit:2.3.0'
29 | implementation project(':android-pdf-viewer')
30 | }
31 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/pdfrenderer/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.pdfrenderer;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.pdfrenderer", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/assets/sample.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/app/src/main/assets/sample.pdf
--------------------------------------------------------------------------------
/app/src/main/assets/sample101.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/app/src/main/assets/sample101.pdf
--------------------------------------------------------------------------------
/app/src/main/java/com/pdfrenderer/PdfLoad.java:
--------------------------------------------------------------------------------
1 | package com.pdfrenderer;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.view.View;
9 | import android.widget.Toast;
10 |
11 | import com.github.pdfviewer.PDFView;
12 |
13 | import java.io.BufferedInputStream;
14 | import java.io.File;
15 | import java.io.FileOutputStream;
16 | import java.io.IOException;
17 | import java.io.InputStream;
18 | import java.text.SimpleDateFormat;
19 | import java.util.Date;
20 | import java.util.Locale;
21 |
22 | import okhttp3.ResponseBody;
23 | import retrofit2.Call;
24 | import retrofit2.Callback;
25 | import retrofit2.Response;
26 | import retrofit2.Retrofit;
27 |
28 | public class PdfLoad extends AppCompatActivity implements View.OnClickListener {
29 |
30 | private static final String FILENAME = "sample.pdf";
31 |
32 |
33 | @Override
34 | protected void onCreate(@Nullable Bundle savedInstanceState) {
35 | super.onCreate(savedInstanceState);
36 | setContentView(R.layout.activity_main);
37 | findViewById(R.id.asset).setOnClickListener(this);
38 | findViewById(R.id.storage).setOnClickListener(this);
39 | findViewById(R.id.download).setOnClickListener(this);
40 |
41 |
42 | }
43 |
44 | private void Pickpdfstorage() {
45 |
46 | Intent intent = new Intent();
47 | intent.setAction(Intent.ACTION_GET_CONTENT);
48 | intent.setType("application/pdf");
49 | startActivityForResult(intent, 10);
50 | }
51 |
52 | private void OpenfileFromAsset() {
53 | File file = new File(getCacheDir(), FILENAME);
54 | if (!file.exists()) {
55 |
56 | try {
57 | InputStream asset = getAssets().open(FILENAME);
58 | FileOutputStream output = null;
59 | output = new FileOutputStream(file);
60 | final byte[] buffer = new byte[1024];
61 | int size;
62 | while ((size = asset.read(buffer)) != -1) {
63 | output.write(buffer, 0, size);
64 | }
65 | asset.close();
66 | output.close();
67 | } catch (IOException e) {
68 | e.printStackTrace();
69 | }
70 |
71 | }
72 |
73 | OpenPdfActivity(file.getAbsolutePath());
74 | }
75 |
76 | private void OpenPdfActivity(String absolutePath) {
77 |
78 | PDFView.with(PdfLoad.this)
79 | .fromfilepath(absolutePath)
80 | .swipeHorizontal(false)
81 | .start();
82 | }
83 |
84 | @Override
85 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
86 | super.onActivityResult(requestCode, resultCode, data);
87 | switch (requestCode) {
88 |
89 | case 10:
90 | // Get the Uri of the selected file
91 | if (resultCode == RESULT_OK) {
92 |
93 | if (null != data.getData()) {
94 |
95 | Uri uri = data.getData();
96 | File file;
97 |
98 | if (uri.getScheme().equals("content")) {
99 |
100 | file = new File(getCacheDir(), data.getData().getLastPathSegment());
101 |
102 | try {
103 | InputStream iStream = getContentResolver().openInputStream(uri);
104 | FileOutputStream output = null;
105 | output = new FileOutputStream(file);
106 | final byte[] buffer = new byte[1024];
107 | int size;
108 | while ((size = iStream.read(buffer)) != -1) {
109 | output.write(buffer, 0, size);
110 | }
111 | iStream.close();
112 | output.close();
113 | } catch (IOException e) {
114 | e.printStackTrace();
115 | }
116 | } else
117 | file = new File(uri.getPath());
118 |
119 |
120 | OpenPdfActivity(file.getAbsolutePath());
121 | }
122 | }
123 | break;
124 | }
125 | }
126 |
127 | @Override
128 | public void onClick(View view) {
129 |
130 | switch (view.getId()) {
131 | case R.id.asset:
132 | OpenfileFromAsset();
133 | break;
134 | case R.id.storage:
135 | Pickpdfstorage();
136 | break;
137 | case R.id.download:
138 | initDownload();
139 | break;
140 | }
141 | }
142 |
143 |
144 | private void initDownload() {
145 |
146 | Retrofit retrofit = new Retrofit.Builder()
147 | .baseUrl("https://www.antennahouse.com/")
148 | .build();
149 |
150 | RetrofitInterface retrofitInterface = retrofit.create(RetrofitInterface.class);
151 |
152 | Call request = retrofitInterface.downloadFile();
153 | request.enqueue(new Callback() {
154 | @Override
155 | public void onResponse(Call call, Response response) {
156 |
157 | try {
158 | ResponseBody body = response.body();
159 |
160 | InputStream iStream = new BufferedInputStream(body.byteStream(), 1024 * 8);
161 |
162 | //creating file name dynamically
163 | SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.US);
164 | Date now = new Date();
165 | String fileName = formatter.format(now) + ".pdf";
166 |
167 | File file = new File(getCacheDir(), fileName);
168 | FileOutputStream output = null;
169 | output = new FileOutputStream(file);
170 | final byte[] buffer = new byte[1024];
171 | int size;
172 | while ((size = iStream.read(buffer)) != -1) {
173 | output.write(buffer, 0, size);
174 | }
175 | iStream.close();
176 | output.close();
177 |
178 | OpenPdfActivity(file.getAbsolutePath());
179 |
180 |
181 | } catch (IOException e) {
182 |
183 | e.printStackTrace();
184 | Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
185 |
186 | }
187 | }
188 |
189 | @Override
190 | public void onFailure(Call call, Throwable t) {
191 | System.out.println("testing");
192 |
193 | }
194 | });
195 |
196 | }
197 |
198 | }
199 |
--------------------------------------------------------------------------------
/app/src/main/java/com/pdfrenderer/RetrofitInterface.java:
--------------------------------------------------------------------------------
1 | package com.pdfrenderer;
2 |
3 | import okhttp3.ResponseBody;
4 | import retrofit2.Call;
5 | import retrofit2.http.GET;
6 | import retrofit2.http.Streaming;
7 |
8 | public interface RetrofitInterface {
9 |
10 | @GET("XSLsample/pdf/sample-link_1.pdf")
11 | @Streaming
12 | Call downloadFile();
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
18 |
19 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Pdfrenderer
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/pdfrenderer/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.pdfrenderer;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.1.4'
11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
12 |
13 |
14 |
15 | // NOTE: Do not place your application dependencies here; they belong
16 | // in the individual module build.gradle files
17 | }
18 | }
19 |
20 | allprojects {
21 | repositories {
22 | google()
23 | jcenter()
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Aug 14 12:11:30 IST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/screenshot_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/screenshot_01.png
--------------------------------------------------------------------------------
/screenshot_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manishkummar21/AndroidPdfViewer/dad3052caa55e2fa48a625bd4800e200f3354bba/screenshot_02.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':android-pdf-viewer'
2 |
--------------------------------------------------------------------------------