48 | The PdfRenderer isn't meant to be a full blown PDF solution.If you need to cover more advanced cases, it probably won't be enough for you.
49 | As an example, it doesn't support annotations and it has issues dealing with password protected and corrupted files.That's why the above mentioned
50 | exceptions are thrown and in the case if any exception is thrown we use the alternative of showing the pdf in webView with a Google Drive pdf URL which
51 | enable us to rectify our issue upto some extent.
52 |
53 |
54 |
55 | ```
56 | private fun setAdapter() {
57 | viewModel.getPdfRenderer(pdfPath) //Path of the pdf you want to render
58 | viewModel.pdfRenderer?.let {
59 | val displayMetrics = DisplayMetrics()
60 | windowManager.defaultDisplay.getMetrics(displayMetrics)
61 | val width = displayMetrics.widthPixels
62 | rvPdfView.layoutManager = LinearLayoutManager(this)
63 | pdfReaderAdapter = PdfReaderAdapter(pdfRenderer, width)
64 | rvPdfView.adapter = pdfReaderAdapter //set your adapter here
65 | } ?: run {
66 | takeActionForPdfRendererNull() //if exception is thrown and renderer is null show it in web view(refer PdfActivity)
67 | }
68 | }
69 | }
70 | ```
71 |
72 | Now coming to point where you set your view of the PDF there you can use [PhotoView library by chris banes](https://github.com/chrisbanes/PhotoView) for
73 | zoom in purpose but the issue that arises here is you'll be able to zoom only a single page instead of whole recycler view so instead of that you can use
74 | [PinchToZoomRecyclerView](https://github.com/aditya09tyagi/AndroidPdfReader/blob/master/app/src/main/java/com/personal/android/androidpdfreader/util/PinchToZoomRecyclerView.kt)
75 | which enables you to perform zoom in ,zoom out ,double tap to zoom action on the whole recycler view just like you can do in any other PDF library.
76 |
77 |
78 | Through pdf renderer you can either opt. to render a single page or else you can use the following code where you create a bitmap for each and every page you render and then use that bitmap as a page of your pdf
79 | ```
80 | fun PdfRenderer.Page.renderAndClose(width: Int) = use {
81 | val bitmap = createBitmap(width)
82 | render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
83 | bitmap
84 | }
85 |
86 | private fun PdfRenderer.Page.createBitmap(bitmapWidth: Int): Bitmap {
87 | val bitmap = Bitmap.createBitmap(
88 | bitmapWidth, (bitmapWidth.toFloat() / width * height).toInt(), Bitmap.Config.ARGB_8888
89 | )
90 |
91 | val canvas = Canvas(bitmap)
92 | canvas.drawColor(Color.WHITE)
93 | canvas.drawBitmap(bitmap, 0f, 0f, null)
94 |
95 | return bitmap
96 | }
97 | ```
98 |
99 | So for further information you can go through the project and you can get a better idea,
100 | basically this project is more like a pdf reader app you can use it to open pdf in you phone through this application .
101 |
102 | Thanks and hope it helps everyone..!!
103 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | maven { url 'https://plugins.gradle.org/m2/' }
4 | maven { url 'https://maven.google.com' }
5 | }
6 | }
7 | apply plugin: 'com.android.application'
8 |
9 | apply plugin: 'kotlin-android'
10 |
11 | apply plugin: 'kotlin-android-extensions'
12 |
13 | apply plugin: 'kotlin-kapt'
14 |
15 |
16 | android {
17 | compileSdkVersion 29
18 | buildToolsVersion "29.0.3"
19 |
20 | defaultConfig {
21 | applicationId "com.personal.android.androidpdfreader"
22 | minSdkVersion 21
23 | targetSdkVersion 29
24 | versionCode 1
25 | versionName "1.0"
26 |
27 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
28 | }
29 |
30 | buildTypes {
31 | release {
32 | minifyEnabled false
33 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
34 | }
35 | }
36 |
37 | aaptOptions {
38 | noCompress "pdf"
39 | }
40 |
41 | compileOptions {
42 | sourceCompatibility JavaVersion.VERSION_1_8
43 | targetCompatibility JavaVersion.VERSION_1_8
44 | }
45 |
46 | }
47 |
48 | dependencies {
49 |
50 | def coroutines_version = '1.3.7'
51 | def lifecycle_version = '2.2.0'
52 | def runtime_permissions_version = '4.6.0'
53 |
54 | implementation fileTree(dir: "libs", include: ["*.jar"])
55 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
56 | implementation 'androidx.core:core-ktx:1.3.1'
57 | implementation 'androidx.appcompat:appcompat:1.2.0'
58 | implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
59 |
60 | //Testing
61 | testImplementation 'junit:junit:4.13'
62 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
63 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
64 |
65 |
66 | //Material Design
67 | implementation 'com.google.android.material:material:1.3.0-alpha02'
68 |
69 |
70 | //Kotlin Coroutines
71 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
72 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
73 |
74 |
75 | // Lifecycle components
76 | implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
77 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
78 | kapt "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
79 |
80 |
81 | //RunTime Permissions
82 | implementation "org.permissionsdispatcher:permissionsdispatcher:$runtime_permissions_version"
83 | kapt "org.permissionsdispatcher:permissionsdispatcher-processor:$runtime_permissions_version"
84 |
85 |
86 | }
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/personal/android/androidpdfreader/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.personal.android.androidpdfreader
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.personal.android.androidpdfreader", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
16 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/personal/android/androidpdfreader/home/PdfActivity.kt:
--------------------------------------------------------------------------------
1 | package com.personal.android.androidpdfreader.home
2 |
3 | import android.Manifest
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.graphics.pdf.PdfRenderer
7 | import android.net.Uri
8 | import android.os.Bundle
9 | import android.util.DisplayMetrics
10 | import android.webkit.WebResourceError
11 | import android.webkit.WebResourceRequest
12 | import android.webkit.WebView
13 | import android.webkit.WebViewClient
14 | import android.widget.Toast
15 | import androidx.appcompat.app.AppCompatActivity
16 | import androidx.lifecycle.ViewModelProvider
17 | import androidx.recyclerview.widget.LinearLayoutManager
18 | import com.personal.android.androidpdfreader.R
19 | import com.personal.android.androidpdfreader.util.*
20 | import kotlinx.android.synthetic.main.activity_pdf.*
21 |
22 |
23 | class PdfActivity : AppCompatActivity(), PermissionCommonUtil.OnPermissionRequestListener {
24 |
25 | private lateinit var pdfReaderAdapter: PdfReaderAdapter
26 | private lateinit var viewModel: PdfViewModel
27 | private var pdfUri: Uri? = null
28 | private lateinit var title: String
29 | private lateinit var pdfPath: String
30 | private lateinit var permissionCommonUtil: PermissionCommonUtil
31 | private val readWritePermissionRequestCode=565
32 | private val permissions = arrayOf(
33 | Manifest.permission.READ_EXTERNAL_STORAGE,
34 | Manifest.permission.WRITE_EXTERNAL_STORAGE
35 | )
36 |
37 | companion object {
38 | fun newIntent(context: Context) = Intent(context, PdfActivity::class.java)
39 | }
40 |
41 | override fun onCreate(savedInstanceState: Bundle?) {
42 | super.onCreate(savedInstanceState)
43 | setContentView(R.layout.activity_pdf)
44 | initPermission()
45 | checkReadWritePermission()
46 | getArguments()
47 | initViewModel()
48 | setAdapter()
49 | }
50 |
51 | private fun initViewModel() {
52 | viewModel = ViewModelProvider(this).get(PdfViewModel::class.java)
53 | }
54 |
55 | private fun initPermission(){
56 | permissionCommonUtil = PermissionCommonUtil(this,permissions,readWritePermissionRequestCode,this)
57 | }
58 |
59 | private fun getArguments(){
60 | intent?.let {
61 | if (it.action == Intent.ACTION_VIEW) {
62 | if (it.data != null) {
63 | try {
64 | pdfUri = it.data
65 | FilePathUtil.getPath(this, it.data!!)?.let {
66 | pdfPath = it
67 | }
68 | title = pdfPath.substringAfterLast("/")
69 | } catch (e: Exception) {
70 | pdfUri?.let {
71 | title = it.path!!
72 | }
73 | }
74 |
75 | }
76 | }
77 | }
78 | }
79 |
80 | private fun setAdapter() {
81 | if (::viewModel.isInitialized && ::pdfPath.isInitialized && ::title.isInitialized) {
82 | toolbar.title = title
83 | viewModel.getPdfRenderer(pdfPath)
84 | viewModel.pdfRenderer?.let {
85 | takeActionForPdfRendererNotNull(it)
86 | } ?: run {
87 | takeActionForPdfRendererNull()
88 | }
89 | }
90 | }
91 |
92 | private fun takeActionForPdfRendererNotNull(pdfRenderer: PdfRenderer) {
93 | val displayMetrics = DisplayMetrics()
94 | windowManager.defaultDisplay.getMetrics(displayMetrics)
95 | val width = displayMetrics.widthPixels
96 | rvPdfView.visible()
97 | pdfWebView.gone()
98 | rvPdfView.layoutManager = LinearLayoutManager(this)
99 | pdfReaderAdapter = PdfReaderAdapter(pdfRenderer, width)
100 | rvPdfView.adapter = pdfReaderAdapter
101 | }
102 |
103 | private fun takeActionForPdfRendererNull() {
104 | rvPdfView.gone()
105 | pdfWebView.visible()
106 | showPdfInWebView()
107 | }
108 |
109 | private fun showPdfInWebView() {
110 | if (::pdfPath.isInitialized && pdfPath.isNotEmpty()) {
111 | pdfWebView.loadUrl(Constants.DEFAULT_GOOGLE_PDF_URL + pdfPath)
112 | setParametersToPdfWebView()
113 | attachWebViewClientToPdfWebView()
114 | }
115 | }
116 |
117 | private fun setParametersToPdfWebView() {
118 | pdfWebView.settings.setSupportZoom(true)
119 | pdfWebView.settings.allowFileAccess = true
120 | pdfWebView.settings.allowUniversalAccessFromFileURLs = true
121 | pdfWebView.settings.displayZoomControls = true
122 | pdfWebView.isHapticFeedbackEnabled = false
123 | pdfWebView.settings.javaScriptEnabled = true
124 | }
125 |
126 | private fun attachWebViewClientToPdfWebView() {
127 | pdfWebView.webViewClient = WebViewClient()
128 | pdfWebView.webViewClient = object : WebViewClient() {
129 | override fun onPageFinished(view: WebView?, url: String?) {
130 | super.onPageFinished(view, url)
131 | if (view?.title == Constants.EMPTY_STRING)
132 | view.reload()
133 | }
134 |
135 | override fun onReceivedError(
136 | view: WebView?,
137 | request: WebResourceRequest?,
138 | error: WebResourceError?
139 | ) {
140 | super.onReceivedError(view, request, error)
141 | }
142 | }
143 | }
144 |
145 |
146 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
147 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
148 | permissionCommonUtil.onPermissionResult(requestCode, permissions, grantResults)
149 | }
150 |
151 | override fun onDestroy() {
152 | super.onDestroy()
153 | if (::viewModel.isInitialized)
154 | viewModel.pdfRenderer?.close()
155 | }
156 |
157 | private fun checkReadWritePermission() {
158 | if (permissionCommonUtil.isReadWritePermissionAvailable()) {
159 | permissionGranted()
160 | } else {
161 | permissionCommonUtil.requestReadWritePermission()
162 | }
163 | }
164 |
165 | override fun permissionGranted() {
166 | Toast.makeText(this,"Permission Granted",Toast.LENGTH_SHORT).show()
167 | }
168 |
169 | override fun permissionDenied() {
170 | Toast.makeText(this,"Please Grant Permission",Toast.LENGTH_SHORT).show()
171 | }
172 |
173 | override fun shouldShowRationalMessage() {
174 | Toast.makeText(this,"Please Allow Permission",Toast.LENGTH_SHORT).show()
175 | }
176 |
177 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/personal/android/androidpdfreader/home/PdfReaderAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.personal.android.androidpdfreader.home
2 |
3 | import android.graphics.pdf.PdfRenderer
4 | import android.view.LayoutInflater
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.personal.android.androidpdfreader.R
8 |
9 | class PdfReaderAdapter(
10 | private val renderer: PdfRenderer,
11 | private val pageWidth: Int
12 | ) : RecyclerView.Adapter() {
13 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PdfReaderViewHolder {
14 | val inflater = LayoutInflater.from(parent.context)
15 | val view = inflater.inflate(R.layout.cell_pdf_page, parent, false)
16 | return PdfReaderViewHolder(view, renderer, pageWidth)
17 | }
18 |
19 | override fun getItemCount(): Int {
20 | return renderer.pageCount
21 | }
22 |
23 | override fun onBindViewHolder(holder: PdfReaderViewHolder, position: Int) {
24 | holder.setPdfPage()
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/personal/android/androidpdfreader/home/PdfReaderViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.personal.android.androidpdfreader.home
2 |
3 | import android.graphics.pdf.PdfRenderer
4 | import android.view.View
5 | import android.widget.ImageView
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.personal.android.androidpdfreader.util.renderAndClose
8 |
9 | class PdfReaderViewHolder(itemView: View, private val pdfRenderer: PdfRenderer, private val pageWidth: Int) :
10 | RecyclerView.ViewHolder(itemView) {
11 |
12 | fun setPdfPage() {
13 | (itemView as ImageView).setImageBitmap(pdfRenderer.openPage(adapterPosition).renderAndClose(pageWidth))
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/personal/android/androidpdfreader/home/PdfViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.personal.android.androidpdfreader.home
2 |
3 | import android.graphics.pdf.PdfRenderer
4 | import android.os.ParcelFileDescriptor
5 | import android.util.Log
6 | import androidx.lifecycle.ViewModel
7 | import java.io.*
8 |
9 | class PdfViewModel : ViewModel() {
10 |
11 | var pdfRenderer: PdfRenderer? = null
12 |
13 | fun getPdfRenderer(filePath: String) {
14 | try {
15 | pdfRenderer = PdfRenderer(
16 | ParcelFileDescriptor.open(
17 | File(filePath),
18 | ParcelFileDescriptor.MODE_READ_ONLY
19 | )
20 | )
21 | } catch (exception: Exception) {
22 | throwExceptionForPdfRenderer(exception)
23 | }
24 | }
25 |
26 | private fun throwExceptionForPdfRenderer(exception: Exception) {
27 | val message: String = when (exception) {
28 | is IOException -> exception.localizedMessage
29 | is SecurityException -> exception.localizedMessage
30 | is FileNotFoundException -> exception.localizedMessage
31 | else -> exception.localizedMessage
32 | }
33 | Log.d("PDF Renderer exception", message)
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/personal/android/androidpdfreader/util/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.personal.android.androidpdfreader.util
2 |
3 | interface Constants {
4 |
5 | companion object{
6 | const val DEFAULT_GOOGLE_PDF_URL = "https://drive.google.com/viewerng/viewer?embedded=true&url="
7 | const val EMPTY_STRING = ""
8 |
9 | }
10 |
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/personal/android/androidpdfreader/util/Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.personal.android.androidpdfreader.util
2 |
3 | import android.view.View
4 |
5 | fun View.gone(){
6 | this.visibility=View.GONE
7 | }
8 |
9 | fun View.visible(){
10 | this.visibility=View.VISIBLE
11 | }
12 |
13 | fun View.inVisible(){
14 | this.visibility=View.INVISIBLE
15 | }
16 |
17 | inline fun T.use(block: (T) -> R): R {
18 | var closed = false
19 | try {
20 | return block(this)
21 | } catch (e: Exception) {
22 | closed = true
23 | try {
24 | close()
25 | } catch (closeException: Exception) {
26 | e.addSuppressed(closeException)
27 | }
28 | throw e
29 | } finally {
30 | if (!closed) {
31 | close()
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/personal/android/androidpdfreader/util/FilePathUtil.kt:
--------------------------------------------------------------------------------
1 | package com.personal.android.androidpdfreader.util
2 |
3 | import android.content.ContentUris
4 | import android.content.Context
5 | import android.database.Cursor
6 | import android.net.Uri
7 | import android.os.Build
8 | import android.os.Environment
9 | import android.provider.DocumentsContract
10 | import android.provider.MediaStore
11 |
12 | object FilePathUtil {
13 | /**
14 | * Method for return file path of Gallery image
15 | *
16 | * @param context
17 | * @param uri
18 | * @return path of the selected image file from gallery
19 | */
20 | fun getPath(context: Context, uri: Uri): String? {
21 |
22 | //check here to KITKAT or new version
23 | val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
24 |
25 | // DocumentProvider
26 | if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
27 |
28 | // ExternalStorageProvider
29 | if (isExternalStorageDocument(uri)) {
30 | val docId = DocumentsContract.getDocumentId(uri)
31 | val split = docId.split(":".toRegex()).toTypedArray()
32 | val type = split[0]
33 | if ("primary".equals(type, ignoreCase = true)) {
34 | return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
35 | }
36 | } else if (isDownloadsDocument(uri)) {
37 | val id = DocumentsContract.getDocumentId(uri)
38 | val contentUri = ContentUris.withAppendedId(
39 | Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)
40 | )
41 | return getDataColumn(context, contentUri, null, null)
42 | } else if (isMediaDocument(uri)) {
43 | val docId = DocumentsContract.getDocumentId(uri)
44 | val split = docId.split(":".toRegex()).toTypedArray()
45 | val type = split[0]
46 | var contentUri: Uri? = null
47 | if ("image" == type) {
48 | contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
49 | } else if ("video" == type) {
50 | contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
51 | } else if ("audio" == type) {
52 | contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
53 | }
54 | val selection = "_id=?"
55 | val selectionArgs = arrayOf(
56 | split[1]
57 | )
58 | return getDataColumn(context, contentUri, selection, selectionArgs)
59 | }
60 | } else if ("content".equals(uri.scheme, ignoreCase = true)) {
61 |
62 | // Return the remote address
63 | return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(
64 | context,
65 | uri,
66 | null,
67 | null
68 | )
69 | } else if ("file".equals(uri.scheme, ignoreCase = true)) {
70 | return uri.path
71 | }
72 | return null
73 | }
74 |
75 | /**
76 | * Get the value of the data column for this Uri. This is useful for
77 | * MediaStore Uris, and other file-based ContentProviders.
78 | *
79 | * @param context The context.
80 | * @param uri The Uri to query.
81 | * @param selection (Optional) Filter used in the query.
82 | * @param selectionArgs (Optional) Selection arguments used in the query.
83 | * @return The value of the _data column, which is typically a file path.
84 | */
85 | fun getDataColumn(
86 | context: Context, uri: Uri?, selection: String?,
87 | selectionArgs: Array?
88 | ): String? {
89 | var cursor: Cursor? = null
90 | val column = "_data"
91 | val projection = arrayOf(
92 | column
93 | )
94 | try {
95 | cursor = context.contentResolver.query(
96 | uri!!, projection, selection, selectionArgs,
97 | null
98 | )
99 | if (cursor != null && cursor.moveToFirst()) {
100 | val index = cursor.getColumnIndexOrThrow(column)
101 | return cursor.getString(index)
102 | }
103 | } finally {
104 | cursor?.close()
105 | }
106 | return null
107 | }
108 |
109 | /**
110 | * @param uri The Uri to check.
111 | * @return Whether the Uri authority is ExternalStorageProvider.
112 | */
113 | fun isExternalStorageDocument(uri: Uri): Boolean {
114 | return "com.android.externalstorage.documents" == uri.authority
115 | }
116 |
117 | /**
118 | * @param uri The Uri to check.
119 | * @return Whether the Uri authority is DownloadsProvider.
120 | */
121 | fun isDownloadsDocument(uri: Uri): Boolean {
122 | return "com.android.providers.downloads.documents" == uri.authority
123 | }
124 |
125 | /**
126 | * @param uri The Uri to check.
127 | * @return Whether the Uri authority is MediaProvider.
128 | */
129 | fun isMediaDocument(uri: Uri): Boolean {
130 | return "com.android.providers.media.documents" == uri.authority
131 | }
132 |
133 | /**
134 | * @param uri The Uri to check.
135 | * @return Whether the Uri authority is Google Photos.
136 | */
137 | fun isGooglePhotosUri(uri: Uri): Boolean {
138 | return "com.google.android.apps.photos.content" == uri.authority
139 | }
140 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/personal/android/androidpdfreader/util/PdfHelper.kt:
--------------------------------------------------------------------------------
1 | package com.personal.android.androidpdfreader.util
2 |
3 | import android.graphics.pdf.PdfRenderer
4 | import android.os.ParcelFileDescriptor
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.withContext
7 | import java.io.File
8 |
9 | suspend fun renderSinglePage(filePath: String, width: Int) = withContext(Dispatchers.IO) {
10 | PdfRenderer(ParcelFileDescriptor.open(File(filePath), ParcelFileDescriptor.MODE_READ_ONLY)).use { renderer ->
11 | renderer.openPage(0).renderAndClose(width)
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/personal/android/androidpdfreader/util/PdfRendererExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.personal.android.androidpdfreader.util
2 |
3 | import android.graphics.Bitmap
4 | import android.graphics.Canvas
5 | import android.graphics.Color
6 | import android.graphics.pdf.PdfRenderer
7 |
8 | fun PdfRenderer.Page.renderAndClose(width: Int) = use {
9 | val bitmap = createBitmap(width)
10 | render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
11 | bitmap
12 | }
13 |
14 | private fun PdfRenderer.Page.createBitmap(bitmapWidth: Int): Bitmap {
15 | val bitmap = Bitmap.createBitmap(
16 | bitmapWidth, (bitmapWidth.toFloat() / width * height).toInt(), Bitmap.Config.ARGB_8888
17 | )
18 |
19 | val canvas = Canvas(bitmap)
20 | canvas.drawColor(Color.WHITE)
21 | canvas.drawBitmap(bitmap, 0f, 0f, null)
22 |
23 | return bitmap
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/personal/android/androidpdfreader/util/PermissionCommonUtil.kt:
--------------------------------------------------------------------------------
1 | package com.personal.android.androidpdfreader.util
2 |
3 | import android.app.Activity
4 | import android.content.pm.PackageManager
5 | import androidx.core.app.ActivityCompat
6 | import androidx.core.content.ContextCompat
7 |
8 | class PermissionCommonUtil(private val activity: Activity,
9 | private val permissions: Array,
10 | private val readWritePermissionRequestCode: Int,
11 | private val listener: OnPermissionRequestListener) {
12 |
13 | fun isReadWritePermissionAvailable(): Boolean {
14 | var isGranted = true
15 | permissions.forEach {
16 | if (ContextCompat.checkSelfPermission(activity, it) != PackageManager.PERMISSION_GRANTED) {
17 | isGranted = false
18 | return@forEach
19 | }
20 | }
21 | return isGranted
22 | }
23 |
24 | fun requestReadWritePermission() {
25 | if (permissions.all { ActivityCompat.shouldShowRequestPermissionRationale(activity, it) }) {
26 | listener.shouldShowRationalMessage()
27 | ActivityCompat.requestPermissions(activity, permissions, readWritePermissionRequestCode)
28 | } else {
29 | // No explanation needed, we can request the permission.
30 | ActivityCompat.requestPermissions(activity, permissions, readWritePermissionRequestCode)
31 | }
32 | }
33 |
34 | fun onPermissionResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
35 | when (requestCode) {
36 | readWritePermissionRequestCode -> {
37 | // If request is cancelled, the result arrays are empty.
38 | if ((grantResults.isNotEmpty() && grantResults.all { it == PackageManager.PERMISSION_GRANTED })) {
39 | listener.permissionGranted()
40 | } else {
41 | listener.permissionDenied()
42 | }
43 | }
44 | else -> {
45 | // Ignore all other requests.
46 | }
47 | }
48 | }
49 |
50 | interface OnPermissionRequestListener {
51 | fun permissionGranted()
52 | fun permissionDenied()
53 | fun shouldShowRationalMessage()
54 | }
55 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/personal/android/androidpdfreader/util/PinchToZoomRecyclerView.kt:
--------------------------------------------------------------------------------
1 | package com.personal.android.androidpdfreader.util
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.util.AttributeSet
6 | import android.view.GestureDetector
7 | import android.view.MotionEvent
8 | import android.view.ScaleGestureDetector
9 | import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener
10 | import androidx.recyclerview.widget.RecyclerView
11 |
12 | class PinchToZoomRecyclerView : RecyclerView {
13 | private var mActivePointerId = INVALID_POINTER_ID
14 | private var mScaleDetector: ScaleGestureDetector? = null
15 | private var mScaleFactor = 1f
16 | private var maxWidth = 0.0f
17 | private var maxHeight = 0.0f
18 | private var mLastTouchX = 0f
19 | private var mLastTouchY = 0f
20 | private var mPosX = 0f
21 | private var mPosY = 0f
22 | private var width = 0f
23 | private var height = 0f
24 | private var minScale = 1f
25 | private var maxScale = 1.5f
26 | private var gestureDetector: GestureDetector? = null
27 |
28 | constructor(context: Context?) : super(context!!) {
29 | if (!isInEditMode) {
30 | mScaleDetector = ScaleGestureDetector(getContext(), ScaleListener())
31 | gestureDetector = GestureDetector(getContext(), GestureListener())
32 | }
33 | }
34 |
35 | constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs) {
36 | if (!isInEditMode) {
37 | mScaleDetector = ScaleGestureDetector(getContext(), ScaleListener())
38 | gestureDetector = GestureDetector(getContext(), GestureListener())
39 | }
40 | }
41 |
42 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context!!, attrs, defStyleAttr) {
43 | if (!isInEditMode) {
44 | mScaleDetector = ScaleGestureDetector(getContext(), ScaleListener())
45 | gestureDetector = GestureDetector(getContext(), GestureListener())
46 | }
47 | }
48 |
49 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
50 | width = MeasureSpec.getSize(widthMeasureSpec).toFloat()
51 | height = MeasureSpec.getSize(heightMeasureSpec).toFloat()
52 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
53 | }
54 |
55 | override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
56 | try {
57 | return super.onInterceptTouchEvent(ev)
58 | } catch (ex: IllegalArgumentException) {
59 | ex.printStackTrace()
60 | }
61 | return false
62 | }
63 |
64 | override fun onTouchEvent(ev: MotionEvent): Boolean {
65 | super.onTouchEvent(ev)
66 | val action = ev.action
67 | mScaleDetector!!.onTouchEvent(ev)
68 | gestureDetector!!.onTouchEvent(ev)
69 | when (action and MotionEvent.ACTION_MASK) {
70 | MotionEvent.ACTION_DOWN -> {
71 | val x = ev.x
72 | val y = ev.y
73 | mLastTouchX = x
74 | mLastTouchY = y
75 | mActivePointerId = ev.getPointerId(0)
76 | }
77 | MotionEvent.ACTION_MOVE -> {
78 |
79 | /* this line is replaced because here came below isssue
80 | java.lang.IllegalArgumentException: pointerIndex out of range
81 | ref http://stackoverflow.com/questions/6919292/pointerindex-out-of-range-android-multitouch
82 | */
83 | //final int pointerIndex = ev.findPointerIndex(mActivePointerId);
84 | val pointerIndex = (action and MotionEvent.ACTION_POINTER_INDEX_MASK
85 | shr MotionEvent.ACTION_POINTER_INDEX_SHIFT)
86 | val x = ev.getX(pointerIndex)
87 | val y = ev.getY(pointerIndex)
88 | val dx = x - mLastTouchX
89 | val dy = y - mLastTouchY
90 | mPosX += dx
91 | mPosY += dy
92 | if (mPosX > 0.0f) mPosX = 0.0f else if (mPosX < maxWidth) mPosX = maxWidth
93 | if (mPosY > 0.0f) mPosY = 0.0f else if (mPosY < maxHeight) mPosY = maxHeight
94 | mLastTouchX = x
95 | mLastTouchY = y
96 | invalidate()
97 | }
98 | MotionEvent.ACTION_UP -> {
99 | mActivePointerId = INVALID_POINTER_ID
100 | }
101 | MotionEvent.ACTION_CANCEL -> {
102 | mActivePointerId = INVALID_POINTER_ID
103 | }
104 | MotionEvent.ACTION_POINTER_UP -> {
105 | val pointerIndex = action and MotionEvent.ACTION_POINTER_INDEX_MASK shr MotionEvent.ACTION_POINTER_INDEX_SHIFT
106 | val pointerId = ev.getPointerId(pointerIndex)
107 | if (pointerId == mActivePointerId) {
108 | val newPointerIndex = if (pointerIndex == 0) 1 else 0
109 | mLastTouchX = ev.getX(newPointerIndex)
110 | mLastTouchY = ev.getY(newPointerIndex)
111 | mActivePointerId = ev.getPointerId(newPointerIndex)
112 | }
113 | }
114 | }
115 | return true
116 | }
117 |
118 | override fun onDraw(canvas: Canvas) {
119 | super.onDraw(canvas)
120 | canvas.save()
121 | canvas.translate(mPosX, mPosY)
122 | canvas.scale(mScaleFactor, mScaleFactor)
123 | canvas.restore()
124 | }
125 |
126 | override fun dispatchDraw(canvas: Canvas) {
127 | canvas.save()
128 | if (mScaleFactor == 1.0f) {
129 | mPosX = 0.0f
130 | mPosY = 0.0f
131 | }
132 | canvas.translate(mPosX, mPosY)
133 | canvas.scale(mScaleFactor, mScaleFactor)
134 | super.dispatchDraw(canvas)
135 | canvas.restore()
136 | invalidate()
137 | }
138 |
139 | private inner class ScaleListener : SimpleOnScaleGestureListener() {
140 | override fun onScale(detector: ScaleGestureDetector): Boolean {
141 | mScaleFactor *= detector.scaleFactor
142 | mScaleFactor = Math.max(1.0f, Math.min(mScaleFactor, 3.0f))
143 | maxWidth = width - width * mScaleFactor
144 | maxHeight = height - height * mScaleFactor
145 | invalidate()
146 | return true
147 | }
148 | }
149 |
150 | private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {
151 | override fun onDoubleTap(e: MotionEvent?): Boolean {
152 | mScaleFactor = if (mScaleFactor == maxScale) {
153 | minScale
154 | } else {
155 | maxScale
156 | }
157 | invalidate()
158 | return false
159 | }
160 | }
161 |
162 | companion object {
163 | private const val INVALID_POINTER_ID = -1
164 | }
165 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/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_pdf.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
23 |
24 |
25 |
26 |
33 |
34 |
39 |
40 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/cell_pdf_page.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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/aditya09tyagi/AndroidPdfReader/7b4d2fa28b88319d2139da3f7d86a5f69203dc75/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aditya09tyagi/AndroidPdfReader/7b4d2fa28b88319d2139da3f7d86a5f69203dc75/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aditya09tyagi/AndroidPdfReader/7b4d2fa28b88319d2139da3f7d86a5f69203dc75/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aditya09tyagi/AndroidPdfReader/7b4d2fa28b88319d2139da3f7d86a5f69203dc75/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aditya09tyagi/AndroidPdfReader/7b4d2fa28b88319d2139da3f7d86a5f69203dc75/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aditya09tyagi/AndroidPdfReader/7b4d2fa28b88319d2139da3f7d86a5f69203dc75/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aditya09tyagi/AndroidPdfReader/7b4d2fa28b88319d2139da3f7d86a5f69203dc75/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aditya09tyagi/AndroidPdfReader/7b4d2fa28b88319d2139da3f7d86a5f69203dc75/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aditya09tyagi/AndroidPdfReader/7b4d2fa28b88319d2139da3f7d86a5f69203dc75/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aditya09tyagi/AndroidPdfReader/7b4d2fa28b88319d2139da3f7d86a5f69203dc75/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
7 | #FFFFFF
8 | #000000
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidPdfReader
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/test/java/com/personal/android/androidpdfreader/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.personal.android.androidpdfreader
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun 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 | buildscript {
3 | ext.kotlin_version = "1.4.0"
4 | ext.services_version = '4.2.0'
5 | repositories {
6 | google()
7 | jcenter()
8 | maven { url 'https://jitpack.io' }
9 | }
10 | dependencies {
11 | classpath "com.android.tools.build:gradle:4.0.1"
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | google()
22 | jcenter()
23 | maven { url 'https://jitpack.io' }
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
--------------------------------------------------------------------------------
/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=-Xmx2048m
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 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aditya09tyagi/AndroidPdfReader/7b4d2fa28b88319d2139da3f7d86a5f69203dc75/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Sep 04 16:51:30 IST 2020
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-6.1.1-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 |
--------------------------------------------------------------------------------
/pdf_reader_demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aditya09tyagi/AndroidPdfReader/7b4d2fa28b88319d2139da3f7d86a5f69203dc75/pdf_reader_demo.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name = "AndroidPdfReader"
--------------------------------------------------------------------------------