Compare commits
16 Commits
fee1b1516f
...
eead76ad86
Author | SHA1 | Date |
---|---|---|
Daniel Micay | eead76ad86 | |
loryeam | fbd6c45c3f | |
Pratyush | 8d0caf65a2 | |
Pratyush | 221e45cd52 | |
Pratyush | ee87e43bcb | |
Pratyush | 5fadf7f47d | |
Pratyush | 7ff831769e | |
Daniel Micay | 9e40a05be3 | |
Pratyush | 18761b3e4a | |
Pratyush | 43f971c028 | |
Pratyush | 2b81d4dcc3 | |
Pratyush | 5a55c8045b | |
Pratyush | 718b448cc5 | |
Pratyush | afaf71a4a2 | |
Pratyush | 8a308b69e5 | |
dependabot[bot] | ea5c34ed52 |
|
@ -41,7 +41,7 @@ android {
|
||||||
applicationId = "app.grapheneos.pdfviewer"
|
applicationId = "app.grapheneos.pdfviewer"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 33
|
targetSdk = 33
|
||||||
versionCode = 15
|
versionCode = 16
|
||||||
versionName = versionCode.toString()
|
versionName = versionCode.toString()
|
||||||
resourceConfigurations.add("en")
|
resourceConfigurations.add("en")
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,15 @@
|
||||||
android:targetSandboxVersion="2">
|
android:targetSandboxVersion="2">
|
||||||
<original-package android:name="org.grapheneos.pdfviewer" />
|
<original-package android:name="org.grapheneos.pdfviewer" />
|
||||||
|
|
||||||
<application android:icon="@mipmap/ic_launcher"
|
<application android:name=".App"
|
||||||
android:roundIcon="@mipmap/ic_launcher"
|
android:allowBackup="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/AppTheme"
|
android:roundIcon="@mipmap/ic_launcher"
|
||||||
android:allowBackup="true">
|
android:theme="@style/AppTheme">
|
||||||
<activity android:name=".PdfViewer"
|
<activity android:name=".PdfViewer"
|
||||||
android:exported="true">
|
android:documentLaunchMode="always"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
|
|
@ -73,7 +73,7 @@ function renderPage(pageNumber, zoom, prerender, prerenderTrigger=0) {
|
||||||
for (let i = 0; i < cache.length; i++) {
|
for (let i = 0; i < cache.length; i++) {
|
||||||
const cached = cache[i];
|
const cached = cache[i];
|
||||||
if (cached.pageNumber === pageNumber && cached.zoomRatio === newZoomRatio &&
|
if (cached.pageNumber === pageNumber && cached.zoomRatio === newZoomRatio &&
|
||||||
cache.orientationDegrees === orientationDegrees) {
|
cached.orientationDegrees === orientationDegrees) {
|
||||||
if (useRender) {
|
if (useRender) {
|
||||||
cache.splice(i, 1);
|
cache.splice(i, 1);
|
||||||
cache.push(cached);
|
cache.push(cached);
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package app.grapheneos.pdfviewer
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import com.google.android.material.color.DynamicColors
|
||||||
|
|
||||||
|
class App : Application() {
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
DynamicColors.applyToActivitiesIfAvailable(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,7 +7,12 @@ import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
@Throws(FileNotFoundException::class, IOException::class)
|
@Throws(
|
||||||
|
FileNotFoundException::class,
|
||||||
|
IOException::class,
|
||||||
|
IllegalArgumentException::class,
|
||||||
|
OutOfMemoryError::class
|
||||||
|
)
|
||||||
fun saveAs(context: Context, existingUri: Uri, saveAs: Uri) {
|
fun saveAs(context: Context, existingUri: Uri, saveAs: Uri) {
|
||||||
|
|
||||||
context.asInputStream(existingUri)?.use { inputStream ->
|
context.asInputStream(existingUri)?.use { inputStream ->
|
||||||
|
|
|
@ -44,6 +44,7 @@ import app.grapheneos.pdfviewer.databinding.PdfviewerBinding;
|
||||||
import app.grapheneos.pdfviewer.fragment.DocumentPropertiesFragment;
|
import app.grapheneos.pdfviewer.fragment.DocumentPropertiesFragment;
|
||||||
import app.grapheneos.pdfviewer.fragment.PasswordPromptFragment;
|
import app.grapheneos.pdfviewer.fragment.PasswordPromptFragment;
|
||||||
import app.grapheneos.pdfviewer.fragment.JumpToPageFragment;
|
import app.grapheneos.pdfviewer.fragment.JumpToPageFragment;
|
||||||
|
import app.grapheneos.pdfviewer.ktx.ViewKt;
|
||||||
import app.grapheneos.pdfviewer.loader.DocumentPropertiesLoader;
|
import app.grapheneos.pdfviewer.loader.DocumentPropertiesLoader;
|
||||||
import app.grapheneos.pdfviewer.viewModel.PasswordStatus;
|
import app.grapheneos.pdfviewer.viewModel.PasswordStatus;
|
||||||
|
|
||||||
|
@ -331,8 +332,7 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||||
if (mUri != null) {
|
if (mUri != null) {
|
||||||
binding.webview.evaluateJavascript("isTextSelected()", selection -> {
|
binding.webview.evaluateJavascript("isTextSelected()", selection -> {
|
||||||
if (!Boolean.parseBoolean(selection)) {
|
if (!Boolean.parseBoolean(selection)) {
|
||||||
if ((getWindow().getDecorView().getSystemUiVisibility() &
|
if (getSupportActionBar().isShowing()) {
|
||||||
View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
|
|
||||||
hideSystemUi();
|
hideSystemUi();
|
||||||
} else {
|
} else {
|
||||||
showSystemUi();
|
showSystemUi();
|
||||||
|
@ -569,21 +569,12 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSystemUi() {
|
private void showSystemUi() {
|
||||||
getWindow().getDecorView().setSystemUiVisibility(
|
ViewKt.showSystemUi(binding.getRoot(), getWindow());
|
||||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
|
||||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
|
||||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
|
||||||
getSupportActionBar().show();
|
getSupportActionBar().show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hideSystemUi() {
|
private void hideSystemUi() {
|
||||||
getWindow().getDecorView().setSystemUiVisibility(
|
ViewKt.hideSystemUi(binding.getRoot(), getWindow());
|
||||||
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);
|
|
||||||
getSupportActionBar().hide();
|
getSupportActionBar().hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,7 +601,7 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
|
||||||
super.onCreateOptionsMenu(menu);
|
super.onCreateOptionsMenu(menu);
|
||||||
MenuInflater inflater = getMenuInflater();
|
MenuInflater inflater = getMenuInflater();
|
||||||
inflater.inflate(R.menu.pdf_viewer, menu);
|
inflater.inflate(R.menu.pdf_viewer, menu);
|
||||||
|
@ -618,7 +609,7 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(@NonNull Menu menu) {
|
||||||
final int[] ids = {R.id.action_jump_to_page, R.id.action_next, R.id.action_previous,
|
final int[] ids = {R.id.action_jump_to_page, R.id.action_next, R.id.action_previous,
|
||||||
R.id.action_first, R.id.action_last, R.id.action_rotate_clockwise,
|
R.id.action_first, R.id.action_last, R.id.action_rotate_clockwise,
|
||||||
R.id.action_rotate_counterclockwise, R.id.action_view_document_properties,
|
R.id.action_rotate_counterclockwise, R.id.action_view_document_properties,
|
||||||
|
@ -718,8 +709,7 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
|
||||||
private void saveDocumentAs(Uri uri) {
|
private void saveDocumentAs(Uri uri) {
|
||||||
try {
|
try {
|
||||||
KtUtilsKt.saveAs(this, mUri, uri);
|
KtUtilsKt.saveAs(this, mUri, uri);
|
||||||
} catch (IOException e) {
|
} catch (IOException | OutOfMemoryError | IllegalArgumentException e) {
|
||||||
e.printStackTrace();
|
|
||||||
snackbar.setText(R.string.error_while_saving).show();
|
snackbar.setText(R.string.error_while_saving).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,14 @@ public class Utils {
|
||||||
return format.format(kb / 1000) + " MB (" + fileSize + " Bytes)";
|
return format.format(kb / 1000) + " MB (" + fileSize + " Bytes)";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int parseIntSafely(String field) throws ParseException {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(field);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new ParseException("Error while parsing int", -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Parse date as per PDF spec (complies with PDF v1.4 to v1.7)
|
// Parse date as per PDF spec (complies with PDF v1.4 to v1.7)
|
||||||
public static String parseDate(String date) throws ParseException {
|
public static String parseDate(String date) throws ParseException {
|
||||||
int position = 0;
|
int position = 0;
|
||||||
|
@ -45,7 +53,7 @@ public class Utils {
|
||||||
if (!TextUtils.isDigitsOnly(field)) {
|
if (!TextUtils.isDigitsOnly(field)) {
|
||||||
throw new ParseException("Invalid year", position);
|
throw new ParseException("Invalid year", position);
|
||||||
}
|
}
|
||||||
int year = Integer.parseInt(field);
|
int year = parseIntSafely(field);
|
||||||
if (year > currentYear) {
|
if (year > currentYear) {
|
||||||
year = currentYear;
|
year = currentYear;
|
||||||
}
|
}
|
||||||
|
@ -66,7 +74,7 @@ public class Utils {
|
||||||
if (!TextUtils.isDigitsOnly(field)) {
|
if (!TextUtils.isDigitsOnly(field)) {
|
||||||
throw new ParseException("Invalid month", position);
|
throw new ParseException("Invalid month", position);
|
||||||
}
|
}
|
||||||
month = Integer.parseInt(field) - 1;
|
month = parseIntSafely(field) - 1;
|
||||||
if (month > 11) {
|
if (month > 11) {
|
||||||
throw new ParseException("Invalid month", position);
|
throw new ParseException("Invalid month", position);
|
||||||
}
|
}
|
||||||
|
@ -77,7 +85,7 @@ public class Utils {
|
||||||
if (!TextUtils.isDigitsOnly(field)) {
|
if (!TextUtils.isDigitsOnly(field)) {
|
||||||
throw new ParseException("Invalid day", position);
|
throw new ParseException("Invalid day", position);
|
||||||
}
|
}
|
||||||
day = Integer.parseInt(field);
|
day = parseIntSafely(field);
|
||||||
if (day > 31) {
|
if (day > 31) {
|
||||||
throw new ParseException("Invalid day", position);
|
throw new ParseException("Invalid day", position);
|
||||||
}
|
}
|
||||||
|
@ -88,7 +96,7 @@ public class Utils {
|
||||||
if (!TextUtils.isDigitsOnly(field)) {
|
if (!TextUtils.isDigitsOnly(field)) {
|
||||||
throw new ParseException("Invalid hours", position);
|
throw new ParseException("Invalid hours", position);
|
||||||
}
|
}
|
||||||
hours = Integer.parseInt(field);
|
hours = parseIntSafely(field);
|
||||||
if (hours > 23) {
|
if (hours > 23) {
|
||||||
throw new ParseException("Invalid hours", position);
|
throw new ParseException("Invalid hours", position);
|
||||||
}
|
}
|
||||||
|
@ -99,7 +107,7 @@ public class Utils {
|
||||||
if (!TextUtils.isDigitsOnly(field)) {
|
if (!TextUtils.isDigitsOnly(field)) {
|
||||||
throw new ParseException("Invalid minutes", position);
|
throw new ParseException("Invalid minutes", position);
|
||||||
}
|
}
|
||||||
minutes = Integer.parseInt(field);
|
minutes = parseIntSafely(field);
|
||||||
if (minutes > 59) {
|
if (minutes > 59) {
|
||||||
throw new ParseException("Invalid minutes", position);
|
throw new ParseException("Invalid minutes", position);
|
||||||
}
|
}
|
||||||
|
@ -110,7 +118,7 @@ public class Utils {
|
||||||
if (!TextUtils.isDigitsOnly(field)) {
|
if (!TextUtils.isDigitsOnly(field)) {
|
||||||
throw new ParseException("Invalid seconds", position);
|
throw new ParseException("Invalid seconds", position);
|
||||||
}
|
}
|
||||||
seconds = Integer.parseInt(field);
|
seconds = parseIntSafely(field);
|
||||||
if (seconds > 59) {
|
if (seconds > 59) {
|
||||||
throw new ParseException("Invalid seconds", position);
|
throw new ParseException("Invalid seconds", position);
|
||||||
}
|
}
|
||||||
|
@ -134,7 +142,7 @@ public class Utils {
|
||||||
if (!TextUtils.isDigitsOnly(field)) {
|
if (!TextUtils.isDigitsOnly(field)) {
|
||||||
throw new ParseException("Invalid UTC offset hours", position);
|
throw new ParseException("Invalid UTC offset hours", position);
|
||||||
}
|
}
|
||||||
offsetHours = Integer.parseInt(field);
|
offsetHours = parseIntSafely(field);
|
||||||
final int offsetHoursMinutes = offsetHours * 100 + offsetMinutes;
|
final int offsetHoursMinutes = offsetHours * 100 + offsetMinutes;
|
||||||
|
|
||||||
// Validate UTC offset (UTC-12:00 to UTC+14:00)
|
// Validate UTC offset (UTC-12:00 to UTC+14:00)
|
||||||
|
@ -157,7 +165,7 @@ public class Utils {
|
||||||
if (!TextUtils.isDigitsOnly(field)) {
|
if (!TextUtils.isDigitsOnly(field)) {
|
||||||
throw new ParseException("Invalid UTC offset minutes", position);
|
throw new ParseException("Invalid UTC offset minutes", position);
|
||||||
}
|
}
|
||||||
offsetMinutes = Integer.parseInt(field);
|
offsetMinutes = parseIntSafely(field);
|
||||||
if (offsetMinutes > 59) {
|
if (offsetMinutes > 59) {
|
||||||
throw new ParseException("Invalid UTC offset minutes", position);
|
throw new ParseException("Invalid UTC offset minutes", position);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package app.grapheneos.pdfviewer.ktx
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.view.Window
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
|
|
||||||
|
private val systemBars = WindowInsetsCompat.Type.statusBars()
|
||||||
|
|
||||||
|
fun View.hideSystemUi(window: Window) {
|
||||||
|
val controller = WindowCompat.getInsetsController(window, this)
|
||||||
|
controller.systemBarsBehavior =
|
||||||
|
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||||
|
controller.hide(systemBars)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.showSystemUi(window: Window) {
|
||||||
|
WindowCompat.getInsetsController(window, this).show(systemBars)
|
||||||
|
}
|
|
@ -132,7 +132,7 @@ public class DocumentPropertiesLoader extends AsyncTaskLoader<List<CharSequence>
|
||||||
final SpannableStringBuilder property = new SpannableStringBuilder(name).append(":\n");
|
final SpannableStringBuilder property = new SpannableStringBuilder(name).append(":\n");
|
||||||
final String value = json != null ? json.optString(specName, "-") : specName;
|
final String value = json != null ? json.optString(specName, "-") : specName;
|
||||||
|
|
||||||
if (specName.endsWith("Date")) {
|
if (specName != null && specName.endsWith("Date")) {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
try {
|
try {
|
||||||
property.append(value.equals("-") ? value : Utils.parseDate(value));
|
property.append(value.equals("-") ? value : Utils.parseDate(value));
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
android:background="#212121"
|
|
||||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
<item name="colorPrimary">#DEFFFFFF</item>
|
<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:navigationBarColor">@android:color/transparent</item>
|
||||||
|
<item name="android:windowLightNavigationBar" tools:ignore="NewApi">false</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.Material3.Dark" />
|
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.Material3.DynamicColors.Dark" />
|
||||||
|
|
||||||
</resources>
|
</resources>
|
|
@ -1,15 +1,16 @@
|
||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
<item name="colorPrimary">#000000</item>
|
<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:navigationBarColor">@android:color/transparent</item>
|
||||||
|
<item name="android:windowLightNavigationBar" tools:ignore="NewApi">true</item>
|
||||||
</style>
|
</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>
|
</resources>
|
|
@ -11,7 +11,7 @@ buildscript {
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("com.android.tools.build:gradle:7.3.0")
|
classpath("com.android.tools.build:gradle:7.3.0")
|
||||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10")
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue