libpdfviewer: update to PdfViewer 15

libpdfviewer
Hardcore Sushi 2022-10-06 18:22:57 +02:00
commit 2766000ebc
Signed by: hardcoresushi
GPG Key ID: AFE384344A45E13A
20 changed files with 160 additions and 65 deletions

View File

@ -32,12 +32,14 @@ android {
}
}
compileSdk = 32
buildToolsVersion = "32.0.0"
compileSdk = 33
buildToolsVersion = "33.0.0"
namespace = "app.grapheneos.pdfviewer"
defaultConfig {
minSdk = 21
targetSdk = 32
targetSdk = 33
resourceConfigurations.add("en")
}
@ -73,6 +75,6 @@ android {
}
dependencies {
implementation("androidx.appcompat:appcompat:1.4.1")
implementation("com.google.android.material:material:1.5.0")
implementation("androidx.appcompat:appcompat:1.5.1")
implementation("com.google.android.material:material:1.6.1")
}

View File

@ -1,7 +1,8 @@
<lint>
<!-- full backups are desired -->
<issue id="AllowBackup">
<ignore path="src/main/AndroidManifest.xml"/>
<!-- overly aggressive check -->
<issue id="VectorPath">
<ignore path="src/main/res/drawable/ic_rotate_left_24dp.xml"/>
<ignore path="src/main/res/drawable/ic_rotate_right_24dp.xml"/>
</issue>
<!-- Google app indexing doesn't make any sense for this app -->

@ -1 +1 @@
Subproject commit 070a365be5a7579f4ddd5a9a2d4efcd281c2d64f
Subproject commit cc3d3bf299ae11f8f72ae8d64cbf19b340f9a996

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.grapheneos.pdfviewer"
android:targetSandboxVersion="2">
<original-package android:name="org.grapheneos.pdfviewer" />

View File

@ -210,6 +210,7 @@ function loadDocument() {
}
loadingTask.promise.then(function (newDoc) {
channel.onLoaded();
pdfDoc = newDoc;
channel.setNumPages(pdfDoc.numPages);
pdfDoc.getMetadata().then(function (data) {

View File

@ -31,6 +31,7 @@ import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
@ -40,7 +41,9 @@ import app.grapheneos.pdfviewer.databinding.PdfviewerBinding;
import app.grapheneos.pdfviewer.fragment.DocumentPropertiesFragment;
import app.grapheneos.pdfviewer.fragment.PasswordPromptFragment;
import app.grapheneos.pdfviewer.fragment.JumpToPageFragment;
import app.grapheneos.pdfviewer.ktx.ViewKt;
import app.grapheneos.pdfviewer.loader.DocumentPropertiesLoader;
import app.grapheneos.pdfviewer.viewModel.PasswordStatus;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -119,8 +122,8 @@ public class PdfViewer implements LoaderManager.LoaderCallbacks<List<CharSequenc
AppCompatActivity activity;
String fileName;
Long fileSize;
private Snackbar snackbar;
private PasswordPromptFragment mPasswordPromptFragment;
public PasswordStatus passwordValidationViewModel;
private class Channel {
@JavascriptInterface
@ -157,16 +160,23 @@ public class PdfViewer implements LoaderManager.LoaderCallbacks<List<CharSequenc
@JavascriptInterface
public void showPasswordPrompt() {
if (getPasswordPromptFragment().isAdded()) {
getPasswordPromptFragment().dismiss();
if (!getPasswordPromptFragment().isAdded()) {
getPasswordPromptFragment().show(activity.getSupportFragmentManager(), PasswordPromptFragment.class.getName());
}
getPasswordPromptFragment().show(activity.getSupportFragmentManager(), PasswordPromptFragment.class.getName());
passwordValidationViewModel.passwordMissing();
}
@JavascriptInterface
public void invalidPassword() {
activity.runOnUiThread(PdfViewer.this::notifyInvalidPassword);
showPasswordPrompt();
activity.runOnUiThread(() -> passwordValidationViewModel.invalid());
}
@JavascriptInterface
public void onLoaded() {
passwordValidationViewModel.validated();
if (getPasswordPromptFragment().isAdded()) {
getPasswordPromptFragment().dismiss();
}
}
@JavascriptInterface
@ -175,15 +185,12 @@ public class PdfViewer implements LoaderManager.LoaderCallbacks<List<CharSequenc
}
}
private void notifyInvalidPassword() {
snackbar.setText(R.string.password_prompt_invalid_password).show();
}
public PdfViewer(@NonNull AppCompatActivity activity) {
this.activity = activity;
binding = PdfviewerBinding.inflate(activity.getLayoutInflater());
activity.setContentView(binding.getRoot());
activity.setSupportActionBar(binding.toolbar);
passwordValidationViewModel = new ViewModelProvider(activity, ViewModelProvider.AndroidViewModelFactory.getInstance(activity.getApplication())).get(PasswordStatus.class);
WindowCompat.setDecorFitsSystemWindows(activity.getWindow(), false);
@ -283,8 +290,7 @@ public class PdfViewer implements LoaderManager.LoaderCallbacks<List<CharSequenc
public boolean onTapUp() {
binding.webview.evaluateJavascript("isTextSelected()", selection -> {
if (!Boolean.parseBoolean(selection)) {
if ((activity.getWindow().getDecorView().getSystemUiVisibility() &
View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
if (activity.getSupportActionBar().isShowing()) {
hideSystemUi();
} else {
showSystemUi();
@ -315,8 +321,6 @@ public class PdfViewer implements LoaderManager.LoaderCallbacks<List<CharSequenc
mTextView.setTextColor(ColorStateList.valueOf(Color.WHITE));
mTextView.setTextSize(18);
mTextView.setPadding(PADDING, 0, PADDING, 0);
snackbar = Snackbar.make(binding.getRoot(), "", Snackbar.LENGTH_LONG);
}
public void onDestroy() {
@ -456,21 +460,12 @@ public class PdfViewer implements LoaderManager.LoaderCallbacks<List<CharSequenc
}
private void showSystemUi() {
activity.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
ViewKt.showSystemUi(binding.getRoot());
activity.getSupportActionBar().show();
}
private void hideSystemUi() {
activity.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE);
ViewKt.hideSystemUi(binding.getRoot());
activity.getSupportActionBar().hide();
}

View File

@ -39,14 +39,13 @@ public class Utils {
final Calendar calendar = Calendar.getInstance();
final int currentYear = calendar.get(Calendar.YEAR);
int year;
// Year is required
String field = date.substring(position += 2, 6);
if (!TextUtils.isDigitsOnly(field)) {
throw new ParseException("Invalid year", position);
}
year = Integer.parseInt(field);
int year = Integer.parseInt(field);
if (year > currentYear) {
year = currentYear;
}

View File

@ -15,15 +15,21 @@ import androidx.fragment.app.DialogFragment
import app.grapheneos.pdfviewer.PdfViewer
import app.grapheneos.pdfviewer.R
import app.grapheneos.pdfviewer.databinding.PasswordDialogFragmentBinding
import app.grapheneos.pdfviewer.viewModel.PasswordStatus
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
class PasswordPromptFragment(private val pdfViewer: PdfViewer) : DialogFragment() {
private lateinit var passwordEditText : EditText
private lateinit var passwordLayout : TextInputLayout
private lateinit var passwordEditText : TextInputEditText
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val passwordPrompt = AlertDialog.Builder(requireContext())
val passwordPrompt = MaterialAlertDialogBuilder(requireContext())
val passwordDialogFragmentBinding =
PasswordDialogFragmentBinding.inflate(LayoutInflater.from(requireContext()))
passwordLayout = passwordDialogFragmentBinding.pdfPasswordTextInputLayout
passwordEditText = passwordDialogFragmentBinding.pdfPasswordEditText
passwordPrompt.setView(passwordDialogFragmentBinding.root)
passwordEditText.addTextChangedListener(object : TextWatcher {
@ -38,15 +44,39 @@ class PasswordPromptFragment(private val pdfViewer: PdfViewer) : DialogFragment(
sendPassword()
true
}
passwordPrompt.setPositiveButton(R.string.open) { _, _ -> sendPassword() }
passwordPrompt.setPositiveButton(R.string.open, null)
passwordPrompt.setNegativeButton(R.string.cancel, null)
val dialog = passwordPrompt.create()
passwordPrompt.setCancelable(false)
isCancelable = false
dialog.setCanceledOnTouchOutside(false)
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
pdfViewer.passwordValidationViewModel.status.observe(
this
) {
when (it) {
PasswordStatus.Status.MissingPassword -> {
passwordEditText.editableText.clear()
passwordDialogFragmentBinding.title.setText(R.string.password_prompt_description)
}
PasswordStatus.Status.InvalidPassword -> {
passwordEditText.editableText.clear()
passwordDialogFragmentBinding.pdfPasswordTextInputLayout.error =
"invalid password"
}
PasswordStatus.Status.Validated -> {
//Activity will dismiss the dialog
}
else -> {
throw NullPointerException("status shouldn't be null")
}
}
}
return dialog
}
private fun updatePositiveButton() {
passwordLayout.error = ""
val btn = (dialog as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE)
btn.isEnabled = passwordEditText.text?.isNotEmpty() ?: false
}
@ -55,7 +85,6 @@ class PasswordPromptFragment(private val pdfViewer: PdfViewer) : DialogFragment(
val password = passwordEditText.text.toString()
if (!TextUtils.isEmpty(password)) {
pdfViewer.loadPdfWithPassword(password)
dialog?.dismiss()
}
}
@ -64,4 +93,11 @@ class PasswordPromptFragment(private val pdfViewer: PdfViewer) : DialogFragment(
updatePositiveButton()
passwordEditText.requestFocus()
}
override fun onResume() {
super.onResume()
(dialog as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
sendPassword()
}
}
}

View File

@ -0,0 +1,19 @@
package app.grapheneos.pdfviewer.ktx
import android.view.View
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
private val systemBars = WindowInsetsCompat.Type.statusBars()
fun View.hideSystemUi() {
val controller = ViewCompat.getWindowInsetsController(this) ?: return
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
controller.hide(systemBars)
}
fun View.showSystemUi() {
ViewCompat.getWindowInsetsController(this)?.show(systemBars)
}

View File

@ -0,0 +1,27 @@
package app.grapheneos.pdfviewer.viewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class PasswordStatus : ViewModel() {
enum class Status {
MissingPassword,
InvalidPassword,
Validated
}
val status: MutableLiveData<Status> = MutableLiveData(Status.MissingPassword)
fun passwordMissing() {
status.postValue(Status.MissingPassword)
}
fun invalid() {
status.postValue(Status.InvalidPassword)
}
fun validated() {
status.postValue(Status.Validated)
}
}

View File

@ -5,7 +5,8 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
@ -17,6 +18,7 @@
android:textSize="20sp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/pdf_password_text_input_layout"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -14,7 +14,6 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#212121"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>

View File

@ -1,13 +1,14 @@
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#DEFFFFFF</item>
<item name="android:statusBarColor">#212121</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowLightNavigationBar" tools:ignore="NewApi">false</item>
</style>
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.Material3.Dark" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.Material3.DynamicColors.Dark" />
</resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">PDF Viewer</string>
<!--<string name="action_settings">Settings</string>-->
<string name="action_previous">Previous page</string>
@ -15,11 +15,10 @@
<string name="document_properties_retrieval_failed">Failed to obtain document metadata</string>
<string name="webview_out_of_date_title">WebView out-of-date</string>
<string name="webview_out_of_date_message">Your current WebView version is %1$d. The WebView should be at least version %2$d for the PDF Viewer to work.</string>
<string name="webview_out_of_date_message" tools:ignore="PluralsCandidate">Your current WebView version is %1$d. The WebView should be at least version %2$d for the PDF Viewer to work.</string>
<string name="password_prompt_hint">Password</string>
<string name="password_prompt_description">Enter the password to decrypt this PDF file</string>
<string name="password_prompt_invalid_password">Invalid password</string>
<string name="open">Open</string>
<string name="cancel">Cancel</string>
</resources>

View File

@ -1,15 +1,16 @@
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#000000</item>
<item name="android:statusBarColor">#212121</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowLightNavigationBar" tools:ignore="NewApi">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.Material3.Dark" />
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.Material3.DynamicColors.Dark" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.Material3.Light" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.Material3.DynamicColors.Light" />
</resources>

View File

@ -1,11 +1,17 @@
buildscript {
repositories {
google()
mavenCentral()
// dependabot cannot handle google()
maven {
url = uri("https://dl.google.com/dl/android/maven2")
}
// dependabot cannot handle mavenCentral()
maven {
url = uri("https://repo.maven.apache.org/maven2")
}
}
dependencies {
classpath("com.android.tools.build:gradle:7.1.3")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21")
classpath("com.android.tools.build:gradle:7.3.0")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20")
}
}

Binary file not shown.

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=29e49b10984e585d8118b7d0bc452f944e386458df27371b49b4ac1dec4b7fda
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
distributionSha256Sum=f6b8596b10cce501591e92f229816aa4046424f3b24d771751b06779d58c8ec4
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

6
gradlew vendored
View File

@ -205,6 +205,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.

14
gradlew.bat vendored
View File

@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal