1
1
package com.rajat.pdfviewer
2
2
3
3
import android.Manifest.permission
4
- import android.app.Activity
5
4
import android.app.AlertDialog
6
5
import android.content.Context
7
6
import android.content.DialogInterface
8
7
import android.content.Intent
9
8
import android.content.pm.PackageManager
9
+ import android.content.res.ColorStateList
10
+ import android.content.res.Configuration
11
+ import android.graphics.Color
10
12
import android.net.Uri
11
13
import android.os.Build
12
14
import android.os.Bundle
13
15
import android.os.Environment
16
+ import android.text.Spannable
17
+ import android.text.SpannableString
14
18
import android.text.TextUtils
19
+ import android.text.style.TextAppearanceSpan
15
20
import android.util.Log
16
21
import android.view.Menu
17
22
import android.view.MenuInflater
18
23
import android.view.MenuItem
19
- import android.view.View
20
24
import android.view.View.GONE
21
25
import android.view.View.VISIBLE
22
26
import android.widget.Toast
@@ -33,13 +37,17 @@ import androidx.core.view.WindowInsetsCompat
33
37
import androidx.core.view.WindowInsetsControllerCompat
34
38
import androidx.core.view.updatePadding
35
39
import androidx.lifecycle.lifecycleScope
40
+ import com.google.android.material.color.MaterialColors
36
41
import com.rajat.pdfviewer.databinding.ActivityPdfViewerBinding
37
42
import com.rajat.pdfviewer.util.FileUtils.createPdfDocumentUri
38
43
import com.rajat.pdfviewer.util.FileUtils.fileFromAsset
39
44
import com.rajat.pdfviewer.util.FileUtils.uriToFile
40
45
import com.rajat.pdfviewer.util.NetworkUtil.checkInternetConnection
41
46
import com.rajat.pdfviewer.util.saveTo
42
47
import java.io.File
48
+ import java.io.FileNotFoundException
49
+ import java.net.SocketTimeoutException
50
+ import java.net.UnknownHostException
43
51
44
52
/* *
45
53
* Created by Rajat on 11,July,2020
@@ -116,28 +124,62 @@ class PdfViewerActivity : AppCompatActivity() {
116
124
val typedArray = theme.obtainStyledAttributes(R .styleable.PdfRendererView_toolbar )
117
125
try {
118
126
// Retrieve attributes safely
119
- val showToolbar = typedArray.getBoolean(R .styleable.PdfRendererView_toolbar_pdfView_showToolbar , true )
120
- val backIcon = typedArray.getDrawable(R .styleable.PdfRendererView_toolbar_pdfView_backIcon )
121
- val titleTextStyle = typedArray.getResourceId(R .styleable.PdfRendererView_toolbar_pdfView_titleTextStyle , - 1 )
127
+ val showToolbar =
128
+ typedArray.getBoolean(R .styleable.PdfRendererView_toolbar_pdfView_showToolbar , true )
129
+ val backIcon =
130
+ typedArray.getDrawable(R .styleable.PdfRendererView_toolbar_pdfView_backIcon )
131
+ val titleTextStyle = typedArray.getResourceId(
132
+ R .styleable.PdfRendererView_toolbar_pdfView_titleTextStyle ,
133
+ - 1
134
+ )
135
+
136
+ // Retrieve action bar tint with a fallback to Material3 colorPrimary
137
+ val toolbarColor = typedArray.getColor(
138
+ R .styleable.PdfRendererView_toolbar_pdfView_toolbarColor ,
139
+ MaterialColors .getColor(
140
+ this ,
141
+ com.google.android.material.R .attr.colorPrimary,
142
+ Color .BLUE
143
+ )
144
+ )
145
+
146
+ // Adjust toolbar color in dark mode
147
+ val isDarkMode = (resources.configuration.uiMode and
148
+ Configuration .UI_MODE_NIGHT_MASK ) == Configuration .UI_MODE_NIGHT_YES
149
+ val adjustedToolbarColor = if (isDarkMode) {
150
+ MaterialColors .getColor(
151
+ this ,
152
+ com.google.android.material.R .attr.colorSurface,
153
+ Color .DKGRAY
154
+ )
155
+ } else {
156
+ toolbarColor
157
+ }
158
+
159
+ binding.myToolbar.setBackgroundColor(adjustedToolbarColor)
122
160
123
- // Retrieve action bar tint safely
124
- val actionBarTint = if (typedArray.hasValue(R .styleable.PdfRendererView_toolbar_pdfView_actionBarTint )) {
125
- typedArray.getColor(R .styleable.PdfRendererView_toolbar_pdfView_actionBarTint , 0 )
126
- } else null
127
161
128
162
// Apply toolbar visibility
129
- binding.myToolbar.visibility = if (showToolbar) View . VISIBLE else View . GONE
163
+ binding.myToolbar.visibility = if (showToolbar) VISIBLE else GONE
130
164
131
165
// Set back icon if available
132
166
backIcon?.let { binding.myToolbar.navigationIcon = it }
133
167
134
- // Apply title text appearance if defined
168
+ // Apply title text appearance safely
135
169
if (titleTextStyle != - 1 ) {
136
- binding.myToolbar.setTitleTextAppearance(this , titleTextStyle)
170
+ val spannable = SpannableString (binding.myToolbar.title)
171
+ val textAppearance = TextAppearanceSpan (this , titleTextStyle)
172
+ spannable.setSpan(
173
+ textAppearance,
174
+ 0 ,
175
+ spannable.length,
176
+ Spannable .SPAN_EXCLUSIVE_EXCLUSIVE
177
+ )
178
+ binding.myToolbar.title = spannable
137
179
}
138
180
139
- // Apply action bar tint only if defined
140
- actionBarTint?. let { binding.myToolbar.setBackgroundColor(it) }
181
+ // Apply action bar tint using backgroundTintList for better theming
182
+ binding.myToolbar.backgroundTintList = ColorStateList .valueOf(toolbarColor)
141
183
142
184
} finally {
143
185
typedArray.recycle()
@@ -212,12 +254,13 @@ class PdfViewerActivity : AppCompatActivity() {
212
254
R .styleable.PdfRendererView_pdfView_backgroundColor ,
213
255
ContextCompat .getColor(applicationContext, android.R .color.white)
214
256
)
215
- binding.parentLayout.setBackgroundColor(backgroundColor)
216
257
217
258
// Set progress bar style
218
- val progressBarStyleResId = typedArray.getResourceId(R .styleable.PdfRendererView_pdfView_progressBar , - 1 )
259
+ val progressBarStyleResId =
260
+ typedArray.getResourceId(R .styleable.PdfRendererView_pdfView_progressBar , - 1 )
219
261
if (progressBarStyleResId != - 1 ) {
220
- binding.progressBar.indeterminateDrawable = ContextCompat .getDrawable(this , progressBarStyleResId)
262
+ binding.progressBar.indeterminateDrawable =
263
+ ContextCompat .getDrawable(this , progressBarStyleResId)
221
264
}
222
265
} finally {
223
266
typedArray.recycle()
@@ -236,28 +279,39 @@ class PdfViewerActivity : AppCompatActivity() {
236
279
237
280
// Load string resources from XML attributes
238
281
val typedArray = obtainStyledAttributes(R .styleable.PdfRendererView_Strings )
239
- error_pdf_corrupted = typedArray.getString(R .styleable.PdfRendererView_Strings_error_pdf_corrupted )
240
- ? : getString(R .string.error_pdf_corrupted)
241
- error_no_internet_connection = typedArray.getString(R .styleable.PdfRendererView_Strings_error_no_internet_connection )
242
- ? : getString(R .string.error_no_internet_connection)
243
- file_saved_successfully = typedArray.getString(R .styleable.PdfRendererView_Strings_file_saved_successfully )
244
- ? : getString(R .string.file_saved_successfully)
245
- file_saved_to_downloads = typedArray.getString(R .styleable.PdfRendererView_Strings_file_saved_to_downloads )
246
- ? : getString(R .string.file_saved_to_downloads)
247
- file_not_downloaded_yet = typedArray.getString(R .styleable.PdfRendererView_Strings_file_not_downloaded_yet )
248
- ? : getString(R .string.file_not_downloaded_yet)
249
- permission_required = typedArray.getString(R .styleable.PdfRendererView_Strings_permission_required )
250
- ? : getString(R .string.permission_required)
251
- permission_required_title = typedArray.getString(R .styleable.PdfRendererView_Strings_permission_required_title )
252
- ? : getString(R .string.permission_required_title)
253
- pdf_viewer_error = typedArray.getString(R .styleable.PdfRendererView_Strings_pdf_viewer_error )
254
- ? : getString(R .string.pdf_viewer_error)
255
- pdf_viewer_retry = typedArray.getString(R .styleable.PdfRendererView_Strings_pdf_viewer_retry )
256
- ? : getString(R .string.pdf_viewer_retry)
257
- pdf_viewer_cancel = typedArray.getString(R .styleable.PdfRendererView_Strings_pdf_viewer_cancel )
258
- ? : getString(R .string.pdf_viewer_cancel)
259
- pdf_viewer_grant = typedArray.getString(R .styleable.PdfRendererView_Strings_pdf_viewer_grant )
260
- ? : getString(R .string.pdf_viewer_grant)
282
+ error_pdf_corrupted =
283
+ typedArray.getString(R .styleable.PdfRendererView_Strings_error_pdf_corrupted )
284
+ ? : getString(R .string.error_pdf_corrupted)
285
+ error_no_internet_connection =
286
+ typedArray.getString(R .styleable.PdfRendererView_Strings_error_no_internet_connection )
287
+ ? : getString(R .string.error_no_internet_connection)
288
+ file_saved_successfully =
289
+ typedArray.getString(R .styleable.PdfRendererView_Strings_file_saved_successfully )
290
+ ? : getString(R .string.file_saved_successfully)
291
+ file_saved_to_downloads =
292
+ typedArray.getString(R .styleable.PdfRendererView_Strings_file_saved_to_downloads )
293
+ ? : getString(R .string.file_saved_to_downloads)
294
+ file_not_downloaded_yet =
295
+ typedArray.getString(R .styleable.PdfRendererView_Strings_file_not_downloaded_yet )
296
+ ? : getString(R .string.file_not_downloaded_yet)
297
+ permission_required =
298
+ typedArray.getString(R .styleable.PdfRendererView_Strings_permission_required )
299
+ ? : getString(R .string.permission_required)
300
+ permission_required_title =
301
+ typedArray.getString(R .styleable.PdfRendererView_Strings_permission_required_title )
302
+ ? : getString(R .string.permission_required_title)
303
+ pdf_viewer_error =
304
+ typedArray.getString(R .styleable.PdfRendererView_Strings_pdf_viewer_error )
305
+ ? : getString(R .string.pdf_viewer_error)
306
+ pdf_viewer_retry =
307
+ typedArray.getString(R .styleable.PdfRendererView_Strings_pdf_viewer_retry )
308
+ ? : getString(R .string.pdf_viewer_retry)
309
+ pdf_viewer_cancel =
310
+ typedArray.getString(R .styleable.PdfRendererView_Strings_pdf_viewer_cancel )
311
+ ? : getString(R .string.pdf_viewer_cancel)
312
+ pdf_viewer_grant =
313
+ typedArray.getString(R .styleable.PdfRendererView_Strings_pdf_viewer_grant )
314
+ ? : getString(R .string.pdf_viewer_grant)
261
315
262
316
typedArray.recycle()
263
317
}
@@ -291,22 +345,27 @@ class PdfViewerActivity : AppCompatActivity() {
291
345
}
292
346
}
293
347
}
348
+
294
349
override fun onError (error : Throwable ) {
295
350
runOnUiThread {
296
351
false .showProgressBar()
297
352
298
353
val errorMessage = when {
299
- error is java.net. UnknownHostException -> error_no_internet_connection
300
- error is java.net. SocketTimeoutException -> " Network timeout! Please check your connection."
301
- error is java.io. FileNotFoundException -> " File not found on the server."
354
+ error is UnknownHostException -> error_no_internet_connection
355
+ error is SocketTimeoutException -> " Network timeout! Please check your connection."
356
+ error is FileNotFoundException -> " File not found on the server."
302
357
error.message?.contains(" Invalid content type received" ) == true ->
303
358
" The server returned a non-PDF file. Please check the URL."
359
+
304
360
error.message?.contains(" Downloaded file is not a valid PDF" ) == true ->
305
361
" The file appears to be corrupted or is not a valid PDF."
362
+
306
363
error.message?.contains(" Incomplete download" ) == true ->
307
364
" The download was incomplete. Please check your internet connection and try again."
365
+
308
366
error.message?.contains(" Failed to download after" ) == true ->
309
367
" Failed to download the PDF after multiple attempts. Please check your internet connection."
368
+
310
369
else -> " An unexpected error occurred: ${error.localizedMessage} "
311
370
}
312
371
@@ -341,8 +400,8 @@ class PdfViewerActivity : AppCompatActivity() {
341
400
342
401
343
402
private fun isRetryable (error : Throwable ): Boolean {
344
- return error is java.net. UnknownHostException ||
345
- error is java.net. SocketTimeoutException ||
403
+ return error is UnknownHostException ||
404
+ error is SocketTimeoutException ||
346
405
error.message?.contains(" Failed to download" ) == true ||
347
406
error.message?.contains(" Incomplete download" ) == true
348
407
}
@@ -543,7 +602,7 @@ class PdfViewerActivity : AppCompatActivity() {
543
602
544
603
private val createFileLauncher =
545
604
registerForActivityResult(ActivityResultContracts .StartActivityForResult ()) { result ->
546
- if (result.resultCode == Activity . RESULT_OK ) {
605
+ if (result.resultCode == RESULT_OK ) {
547
606
result.data?.data?.let { uri ->
548
607
contentResolver.openOutputStream(uri)?.use { outputStream ->
549
608
downloadedFilePath?.let { filePath ->
0 commit comments