Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.OnApplyWindowInsetsListener;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
Expand Down Expand Up @@ -125,6 +129,10 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
private float mZoomRatio = 1f;
private float mZoomFocusX = 0f;
private float mZoomFocusY = 0f;
private float mInsetLeft = 0f;
private float mInsetTop = 0f;
private float mInsetRight = 0f;
private float mInsetBottom = 0f;
private int mDocumentOrientationDegrees;
private int mDocumentState;
private String mEncryptedDocumentPassword;
Expand All @@ -138,6 +146,13 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
private PasswordPromptFragment mPasswordPromptFragment;
public PdfViewModel viewModel;

private final View.OnLayoutChangeListener appBarOnLayoutChangeListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
if (binding.toolbar.getVisibility() == View.VISIBLE) {
mInsetTop = bottom - top;
}
};

private final ActivityResultLauncher<Intent> openDocumentLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(), result -> {
if (result == null) return;
Expand Down Expand Up @@ -218,6 +233,26 @@ public float getMaxZoomRatio() {
return MAX_ZOOM_RATIO;
}

@JavascriptInterface
public float getInsetLeft() {
return mInsetLeft;
}

@JavascriptInterface
public float getInsetTop() {
return mInsetTop;
}

@JavascriptInterface
public float getInsetRight() {
return mInsetRight;
}

@JavascriptInterface
public float getInsetBottom() {
return mInsetBottom;
}

@JavascriptInterface
public int getDocumentOrientationDegrees() {
return mDocumentOrientationDegrees;
Expand Down Expand Up @@ -279,7 +314,7 @@ private void showWebViewCrashed() {
@SuppressLint({"SetJavaScriptEnabled"})
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
WindowCompat.enableEdgeToEdge(getWindow());

binding = PdfviewerBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
Expand Down Expand Up @@ -308,6 +343,21 @@ protected void onCreate(Bundle savedInstanceState) {
// Margins for the toolbar are needed, so that content of the toolbar
// is not covered by a system button navigation bar when in landscape.
KtUtilsKt.applySystemBarMargins(binding.toolbar, false);
ViewCompat.setOnApplyWindowInsetsListener(
binding.webview, new OnApplyWindowInsetsListener() {
@Override
public @NonNull WindowInsetsCompat onApplyWindowInsets(
@NonNull View v, @NonNull WindowInsetsCompat insets) {
Insets allInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()
| WindowInsetsCompat.Type.displayCutout());
mInsetLeft = allInsets.left;
mInsetRight = allInsets.right;
// Only set the bottom inset. The top will use the height of the app bar layout
// which includes the status bar/display cutout.
mInsetBottom = allInsets.bottom;
return insets;
}
});

binding.webview.setBackgroundColor(Color.TRANSPARENT);

Expand Down Expand Up @@ -503,6 +553,8 @@ public void onZoomEnd() {
mEncryptedDocumentPassword = savedInstanceState.getString(STATE_ENCRYPTED_DOCUMENT_PASSWORD);
}

binding.appBarLayout.addOnLayoutChangeListener(appBarOnLayoutChangeListener);

binding.webviewAlertReload.setOnClickListener(v -> {
webViewCrashed = false;
recreate();
Expand All @@ -529,6 +581,7 @@ private void purgeWebView() {
@Override
protected void onDestroy() {
super.onDestroy();
binding.appBarLayout.removeOnLayoutChangeListener(appBarOnLayoutChangeListener);
purgeWebView();
maybeCloseInputStream();
}
Expand Down
12 changes: 6 additions & 6 deletions app/src/main/res/layout/pdfviewer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
android:layout_width="match_parent"
android:layout_height="match_parent">

<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />

<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
Expand All @@ -18,12 +24,6 @@

</com.google.android.material.appbar.AppBarLayout>

<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />

<ScrollView
android:id="@+id/webview_alert_layout"
android:layout_width="match_parent"
Expand Down
25 changes: 23 additions & 2 deletions viewer/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,23 @@ function display(newCanvas, zoom) {
}

function setLayerTransform(pageWidth, pageHeight, layerDiv) {
const cs = globalThis.getComputedStyle(canvas);
const insetLeft = parseFloat(cs.paddingLeft) || 0;
const insetTop = parseFloat(cs.paddingTop) || 0;
const insetRight = parseFloat(cs.paddingRight) || 0;
const insetBottom = parseFloat(cs.paddingBottom) || 0;

const isOverflownY = canvas.clientHeight > document.body.clientHeight;
const isOverflownX = canvas.clientWidth > document.body.clientWidth;
// Translate the text layer to stay aligned with the rendered page including canvas insets and
// grid centering effects.
const translate = {
X: Math.max(0, pageWidth - document.body.clientWidth) / 2,
Y: Math.max(0, pageHeight - document.body.clientHeight) / 2
X: isOverflownX
? insetLeft - (document.body.clientWidth - pageWidth) / 2
: (insetLeft - insetRight) / 2,
Y: isOverflownY
? insetTop - (document.body.clientHeight - pageHeight) / 2
: (insetTop - insetBottom) / 2
};
layerDiv.style.translate = `${translate.X}px ${translate.Y}px`;
}
Expand Down Expand Up @@ -275,6 +289,13 @@ function renderPage(pageNumber, zoom, prerender, prerenderTrigger = 0) {
const newContext = newCanvas.getContext("2d", { alpha: false });
newContext.scale(ratio, ratio);

// Add padding to the canvas to allow the page to be scrolled bellow/above any
// system/app ui that might be visible.
canvas.style.paddingLeft = (channel.getInsetLeft() / ratio) + "px";
canvas.style.paddingTop = (channel.getInsetTop() / ratio) + "px";
canvas.style.paddingRight = (channel.getInsetRight() / ratio) + "px";
canvas.style.paddingBottom = (channel.getInsetBottom() / ratio) + "px";

task = page.render({
canvasContext: newContext,
viewport: newViewport
Expand Down