From 15adb86de9c42d9b4d9737a6b60d9508b64c8ca6 Mon Sep 17 00:00:00 2001
From: Robin
Date: Sat, 21 Jul 2018 20:55:41 +0200
Subject: [PATCH 1/4] Add initial state saving. Does not work properly for
configuration change
---
.../github/barteksc/pdfviewer/PDFView.java | 174 +++++++++++++++++-
1 file changed, 173 insertions(+), 1 deletion(-)
diff --git a/android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/PDFView.java b/android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/PDFView.java
index 5e9636e7..bd828f52 100644
--- a/android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/PDFView.java
+++ b/android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/PDFView.java
@@ -31,6 +31,9 @@
import android.net.Uri;
import android.os.AsyncTask;
import android.os.HandlerThread;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.RelativeLayout;
@@ -68,6 +71,7 @@
import java.io.File;
import java.io.InputStream;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -124,6 +128,9 @@ enum ScrollDir {
PdfFile pdfFile;
+ private PdfViewState restoredState;
+ private boolean hasRestoredFromState;
+
/** The index of the current sequence */
private int currentPage;
@@ -261,6 +268,21 @@ public PDFView(Context context, AttributeSet set) {
setWillNotDraw(false);
}
+ @Nullable
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ SavedState state = new SavedState(super.onSaveInstanceState());
+ state.viewState = getCurrentViewState();
+ return state;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable parcelable) {
+ SavedState state = (SavedState) parcelable;
+ restoredState = state.viewState;
+ super.onRestoreInstanceState(state.getSuperState());
+ }
+
private void load(DocumentSource docSource, String password) {
load(docSource, password, null);
}
@@ -277,6 +299,52 @@ private void load(DocumentSource docSource, String password, int[] userPages) {
decodingAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
+ /**
+ * Gets the current position and zoom state of the view.
+ * This state can be saved or persisted and later restored with {@link #restoreViewState(PdfViewState)}
+ */
+ public PdfViewState getCurrentViewState() {
+ return new PdfViewState(this);
+ }
+
+ /**
+ * Restores a view state previously gotten with {@link #getCurrentViewState()} or
+ * {@link #onSaveInstanceState()}.
+ * The view handles restoring state with onSaveInstanceState for configuration changes or
+ * activity destruction, but the host application has to make sure the same pdf is loaded.
+ * This view has no way of knowing if the pdf loaded after restoring is the same as before.
+ * If the pdf content changed or a different pdf is loaded, it may restore to a incorrect position.
+ * To disable the automatic restoration call {@link #setSaveEnabled(boolean)}.
+ * Note: This must be called after a pdf file has been loaded. A good place to call this is
+ * in the loadComplete callback.
+ * @throws IllegalStateException If pdf file is not loaded
+ */
+ public void restoreViewState(PdfViewState state) {
+ if (pdfFile == null) {
+ throw new IllegalStateException("Pdf file has not been loaded. Call restoreViewState from the loadCompleted callback.");
+ }
+ zoom = state.zoom;
+ int maxPageCount = pdfFile.getPagesCount() - 1;
+ currentPage = Math.min(maxPageCount, state.currentPage);
+ // since view and page sizes might have changed in a different configuration we cant just restore x/y offsets
+ if (swipeVertical) {
+ float maxXOffset = toCurrentScale(pdfFile.getMaxPageWidth()) - getWidth();
+ currentXOffset = Math.min(maxXOffset, state.offsetX);
+ float newYOffset = pdfFile.getPageOffset(currentPage, zoom) - state.offsetY;
+ float maxYOffset = pdfFile.getDocLen(zoom) - getHeight();
+ currentYOffset = -Math.min(maxYOffset, newYOffset);
+ } else {
+ float newXOffset = pdfFile.getPageOffset(currentPage, zoom) - state.offsetX;
+ float maxXOffset = pdfFile.getDocLen(zoom) - getWidth();
+ currentXOffset = -Math.min(maxXOffset, newXOffset);
+ float maxYOffset = toCurrentScale(pdfFile.getMaxPageHeight()) - getHeight();
+ currentYOffset = Math.min(maxYOffset, state.offsetY);
+ }
+
+ showPage(currentPage);
+ hasRestoredFromState = true;
+ }
+
/**
* Go to the given page.
*
@@ -735,9 +803,17 @@ void loadComplete(PdfFile pdfFile) {
dragPinchManager.enable();
+ hasRestoredFromState = false;
callbacks.callOnLoadComplete(pdfFile.getPagesCount());
- jumpTo(defaultPage, false);
+ if (!hasRestoredFromState && restoredState != null) {
+ restoreViewState(restoredState);
+ restoredState = null;
+ }
+
+ if (!hasRestoredFromState) {
+ jumpTo(defaultPage, false);
+ }
}
void loadError(Throwable t) {
@@ -1280,6 +1356,7 @@ public Configurator fromSource(DocumentSource docSource) {
private enum State {DEFAULT, LOADED, SHOWN, ERROR}
+ @SuppressWarnings("unused")
public class Configurator {
private final DocumentSource documentSource;
@@ -1508,4 +1585,99 @@ public void load() {
}
}
}
+
+ /**
+ * Holds the zoom and position state for PdfView
+ */
+ public static class PdfViewState implements Serializable, Parcelable {
+
+ int currentPage;
+ float zoom = 1f;
+ float offsetX;
+ float offsetY;
+
+ public PdfViewState() {
+ }
+
+ public PdfViewState(PDFView view) {
+ currentPage = view.getCurrentPage();
+ zoom = view.getZoom();
+ offsetX = view.getCurrentXOffset();
+ offsetY = view.getCurrentYOffset();
+ if (view.pdfFile != null) {
+ float pageOffset = view.pdfFile.getPageOffset(currentPage, zoom);
+ if (view.swipeVertical) {
+ offsetY += pageOffset;
+ } else {
+ offsetX += pageOffset;
+ }
+ }
+ }
+
+ private PdfViewState(Parcel in) {
+ currentPage = in.readInt();
+ zoom = in.readFloat();
+ offsetX = in.readFloat();
+ offsetY = in.readFloat();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(currentPage);
+ out.writeFloat(zoom);
+ out.writeFloat(offsetX);
+ out.writeFloat(offsetY);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator CREATOR =
+ new Parcelable.Creator() {
+ public PdfViewState createFromParcel(Parcel in) {
+ return new PdfViewState(in);
+ }
+
+ public PdfViewState[] newArray(int size) {
+ return new PdfViewState[size];
+ }
+ };
+ }
+
+ /**
+ * Wrapper around {@link PdfViewState} to not leak super state in public API.
+ */
+ static class SavedState extends BaseSavedState {
+
+ PdfViewState viewState;
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ viewState = in.readParcelable(getClass().getClassLoader());
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeParcelable(viewState, 0);
+ }
+
+ public static final Parcelable.Creator CREATOR =
+ new Parcelable.Creator() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+
+ }
}
From 9b02875ff0aced06f8a9011ed37ce91ec62fe225 Mon Sep 17 00:00:00 2001
From: Robin
Date: Sun, 29 Jul 2018 16:38:19 +0200
Subject: [PATCH 2/4] Save position as fractions. Finish state-saving api.
---
.../github/barteksc/pdfviewer/PDFView.java | 94 +++++++++++--------
1 file changed, 57 insertions(+), 37 deletions(-)
diff --git a/android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/PDFView.java b/android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/PDFView.java
index bd828f52..a2cdbd73 100644
--- a/android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/PDFView.java
+++ b/android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/PDFView.java
@@ -288,7 +288,6 @@ private void load(DocumentSource docSource, String password) {
}
private void load(DocumentSource docSource, String password, int[] userPages) {
-
if (!recycled) {
throw new IllegalStateException("Don't call load on a PDF View without recycling it first.");
}
@@ -301,10 +300,23 @@ private void load(DocumentSource docSource, String password, int[] userPages) {
/**
* Gets the current position and zoom state of the view.
- * This state can be saved or persisted and later restored with {@link #restoreViewState(PdfViewState)}
+ * This state can be saved or persisted and later restored with {@link #restoreViewState(PdfViewState)}.
*/
public PdfViewState getCurrentViewState() {
- return new PdfViewState(this);
+ PdfViewState state = new PdfViewState();
+ state.zoom = getZoom();
+ state.currentPage = getCurrentPage();
+ float centerX = -currentXOffset + (getWidth() / 2f);
+ float centerY = -currentYOffset + (getHeight() / 2f);
+ SizeF pageSize = pdfFile.getScaledPageSize(state.currentPage, state.zoom);
+ if (swipeVertical) {
+ state.pageFocusX = (centerX - pdfFile.getSecondaryPageOffset(state.currentPage, zoom)) / pageSize.getWidth();
+ state.pageFocusY = (centerY - pdfFile.getPageOffset(state.currentPage, zoom)) / pageSize.getHeight();
+ } else {
+ state.pageFocusX = (centerX - pdfFile.getPageOffset(state.currentPage, zoom)) / pageSize.getWidth();
+ state.pageFocusY = (centerY - pdfFile.getSecondaryPageOffset(state.currentPage, zoom)) / pageSize.getHeight();
+ }
+ return state;
}
/**
@@ -314,9 +326,9 @@ public PdfViewState getCurrentViewState() {
* activity destruction, but the host application has to make sure the same pdf is loaded.
* This view has no way of knowing if the pdf loaded after restoring is the same as before.
* If the pdf content changed or a different pdf is loaded, it may restore to a incorrect position.
- * To disable the automatic restoration call {@link #setSaveEnabled(boolean)}.
+ * To disable the automatic restoration use {@link #setSaveEnabled(boolean)}.
* Note: This must be called after a pdf file has been loaded. A good place to call this is
- * in the loadComplete callback.
+ * in the {@link OnLoadCompleteListener} callback.
* @throws IllegalStateException If pdf file is not loaded
*/
public void restoreViewState(PdfViewState state) {
@@ -326,22 +338,29 @@ public void restoreViewState(PdfViewState state) {
zoom = state.zoom;
int maxPageCount = pdfFile.getPagesCount() - 1;
currentPage = Math.min(maxPageCount, state.currentPage);
- // since view and page sizes might have changed in a different configuration we cant just restore x/y offsets
+
+ SizeF pageSize = pdfFile.getScaledPageSize(currentPage, zoom);
+ float pageX = pageSize.getWidth() * state.pageFocusX;
+ float pageY = pageSize.getHeight() * state.pageFocusY;
+ float mainOffset = pdfFile.getPageOffset(currentPage, zoom);
+ float secondaryOffset = pdfFile.getSecondaryPageOffset(currentPage, zoom);
if (swipeVertical) {
+ float newXOffset = secondaryOffset + pageX - (getWidth() / 2f);
float maxXOffset = toCurrentScale(pdfFile.getMaxPageWidth()) - getWidth();
- currentXOffset = Math.min(maxXOffset, state.offsetX);
- float newYOffset = pdfFile.getPageOffset(currentPage, zoom) - state.offsetY;
+ currentXOffset = -MathUtils.limit(newXOffset, 0, maxXOffset);
+ float newYOffset = mainOffset + pageY - (getHeight() / 2f);
float maxYOffset = pdfFile.getDocLen(zoom) - getHeight();
- currentYOffset = -Math.min(maxYOffset, newYOffset);
+ currentYOffset = -MathUtils.limit(newYOffset, 0, maxYOffset);
} else {
- float newXOffset = pdfFile.getPageOffset(currentPage, zoom) - state.offsetX;
+ float newXOffset = mainOffset + pageX - (getWidth() / 2f);
float maxXOffset = pdfFile.getDocLen(zoom) - getWidth();
- currentXOffset = -Math.min(maxXOffset, newXOffset);
+ currentXOffset = -MathUtils.limit(newXOffset, 0, maxXOffset);
+ float newYOffset = secondaryOffset + pageY - (getHeight() / 2f);
float maxYOffset = toCurrentScale(pdfFile.getMaxPageHeight()) - getHeight();
- currentYOffset = Math.min(maxYOffset, state.offsetY);
+ currentYOffset = -MathUtils.limit(newYOffset, 0, maxYOffset);
}
-
showPage(currentPage);
+ performPageSnap(false);
hasRestoredFromState = true;
}
@@ -973,6 +992,13 @@ void loadPageByOffset() {
* Animate to the nearest snapping position for the current SnapPolicy
*/
public void performPageSnap() {
+ performPageSnap(true);
+ }
+
+ /**
+ * Snap to the nearest snapping position for the current SnapPolicy
+ */
+ public void performPageSnap(boolean animated) {
if (!pageSnap || pdfFile == null || pdfFile.getPagesCount() == 0) {
return;
}
@@ -983,10 +1009,18 @@ public void performPageSnap() {
}
float offset = snapOffsetForPage(centerPage, edge);
- if (swipeVertical) {
- animationManager.startYAnimation(currentYOffset, -offset);
+ if (animated) {
+ if (swipeVertical) {
+ animationManager.startYAnimation(currentYOffset, -offset);
+ } else {
+ animationManager.startXAnimation(currentXOffset, -offset);
+ }
} else {
- animationManager.startXAnimation(currentXOffset, -offset);
+ if (swipeVertical) {
+ moveTo(currentXOffset, -offset);
+ } else {
+ moveTo(-offset, currentYOffset);
+ }
}
}
@@ -1593,40 +1627,26 @@ public static class PdfViewState implements Serializable, Parcelable {
int currentPage;
float zoom = 1f;
- float offsetX;
- float offsetY;
+ // relative point of the page (0 to 1) that is in the center of the view
+ float pageFocusX;
+ float pageFocusY;
public PdfViewState() {
}
- public PdfViewState(PDFView view) {
- currentPage = view.getCurrentPage();
- zoom = view.getZoom();
- offsetX = view.getCurrentXOffset();
- offsetY = view.getCurrentYOffset();
- if (view.pdfFile != null) {
- float pageOffset = view.pdfFile.getPageOffset(currentPage, zoom);
- if (view.swipeVertical) {
- offsetY += pageOffset;
- } else {
- offsetX += pageOffset;
- }
- }
- }
-
private PdfViewState(Parcel in) {
currentPage = in.readInt();
zoom = in.readFloat();
- offsetX = in.readFloat();
- offsetY = in.readFloat();
+ pageFocusX = in.readFloat();
+ pageFocusY = in.readFloat();
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(currentPage);
out.writeFloat(zoom);
- out.writeFloat(offsetX);
- out.writeFloat(offsetY);
+ out.writeFloat(pageFocusX);
+ out.writeFloat(pageFocusY);
}
@Override
From d0702f301996696f8a70e2d4c0ba6d9612f787cc Mon Sep 17 00:00:00 2001
From: Robin
Date: Mon, 13 Aug 2018 21:08:02 +0200
Subject: [PATCH 3/4] Fix position offset and handle null pdf file
---
.../github/barteksc/pdfviewer/PDFView.java | 36 ++++++++++---------
1 file changed, 20 insertions(+), 16 deletions(-)
diff --git a/android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/PDFView.java b/android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/PDFView.java
index a2cdbd73..be4db996 100644
--- a/android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/PDFView.java
+++ b/android-pdf-viewer/src/main/java/com/github/barteksc/pdfviewer/PDFView.java
@@ -272,7 +272,9 @@ public PDFView(Context context, AttributeSet set) {
@Override
protected Parcelable onSaveInstanceState() {
SavedState state = new SavedState(super.onSaveInstanceState());
- state.viewState = getCurrentViewState();
+ if (pdfFile != null) {
+ state.viewState = getCurrentViewState();
+ }
return state;
}
@@ -303,6 +305,9 @@ private void load(DocumentSource docSource, String password, int[] userPages) {
* This state can be saved or persisted and later restored with {@link #restoreViewState(PdfViewState)}.
*/
public PdfViewState getCurrentViewState() {
+ if (pdfFile == null) {
+ throw new IllegalStateException("Failed to save current view state (no file loaded)");
+ }
PdfViewState state = new PdfViewState();
state.zoom = getZoom();
state.currentPage = getCurrentPage();
@@ -344,21 +349,15 @@ public void restoreViewState(PdfViewState state) {
float pageY = pageSize.getHeight() * state.pageFocusY;
float mainOffset = pdfFile.getPageOffset(currentPage, zoom);
float secondaryOffset = pdfFile.getSecondaryPageOffset(currentPage, zoom);
+ float x, y;
if (swipeVertical) {
- float newXOffset = secondaryOffset + pageX - (getWidth() / 2f);
- float maxXOffset = toCurrentScale(pdfFile.getMaxPageWidth()) - getWidth();
- currentXOffset = -MathUtils.limit(newXOffset, 0, maxXOffset);
- float newYOffset = mainOffset + pageY - (getHeight() / 2f);
- float maxYOffset = pdfFile.getDocLen(zoom) - getHeight();
- currentYOffset = -MathUtils.limit(newYOffset, 0, maxYOffset);
+ x = secondaryOffset + pageX - (getWidth() / 2f);
+ y = mainOffset + pageY - (getHeight() / 2f);
} else {
- float newXOffset = mainOffset + pageX - (getWidth() / 2f);
- float maxXOffset = pdfFile.getDocLen(zoom) - getWidth();
- currentXOffset = -MathUtils.limit(newXOffset, 0, maxXOffset);
- float newYOffset = secondaryOffset + pageY - (getHeight() / 2f);
- float maxYOffset = toCurrentScale(pdfFile.getMaxPageHeight()) - getHeight();
- currentYOffset = -MathUtils.limit(newYOffset, 0, maxYOffset);
+ x = mainOffset + pageX - (getWidth() / 2f);
+ y = secondaryOffset + pageY - (getHeight() / 2f);
}
+ moveTo(-x, -y);
showPage(currentPage);
performPageSnap(false);
hasRestoredFromState = true;
@@ -1671,7 +1670,7 @@ public PdfViewState[] newArray(int size) {
*/
static class SavedState extends BaseSavedState {
- PdfViewState viewState;
+ @Nullable PdfViewState viewState;
public SavedState(Parcelable superState) {
super(superState);
@@ -1679,13 +1678,18 @@ public SavedState(Parcelable superState) {
private SavedState(Parcel in) {
super(in);
- viewState = in.readParcelable(getClass().getClassLoader());
+ if (in.readByte() == 1) {
+ viewState = in.readParcelable(getClass().getClassLoader());
+ }
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
- out.writeParcelable(viewState, 0);
+ out.writeByte((byte) (viewState == null ? 0 : 1));
+ if (viewState != null) {
+ out.writeParcelable(viewState, 0);
+ }
}
public static final Parcelable.Creator CREATOR =
From e3d3be1ad25254fefcc36bd17032e579021b6dbb Mon Sep 17 00:00:00 2001
From: Robin
Date: Mon, 13 Aug 2018 21:08:58 +0200
Subject: [PATCH 4/4] Remove unnecessary page restoration from sample activity
---
.../java/com/github/barteksc/sample/PDFViewActivity.java | 6 ------
1 file changed, 6 deletions(-)
diff --git a/sample/src/main/java/com/github/barteksc/sample/PDFViewActivity.java b/sample/src/main/java/com/github/barteksc/sample/PDFViewActivity.java
index d23b0008..75c65cee 100755
--- a/sample/src/main/java/com/github/barteksc/sample/PDFViewActivity.java
+++ b/sample/src/main/java/com/github/barteksc/sample/PDFViewActivity.java
@@ -66,9 +66,6 @@ public class PDFViewActivity extends AppCompatActivity implements OnPageChangeLi
@NonConfigurationInstance
Uri uri;
- @NonConfigurationInstance
- Integer pageNumber = 0;
-
String pdfFileName;
@OptionsItem(R.id.pickFile)
@@ -115,7 +112,6 @@ private void displayFromAsset(String assetFileName) {
pdfFileName = assetFileName;
pdfView.fromAsset(SAMPLE_FILE)
- .defaultPage(pageNumber)
.onPageChange(this)
.enableAnnotationRendering(true)
.onLoad(this)
@@ -130,7 +126,6 @@ private void displayFromUri(Uri uri) {
pdfFileName = getFileName(uri);
pdfView.fromUri(uri)
- .defaultPage(pageNumber)
.onPageChange(this)
.enableAnnotationRendering(true)
.onLoad(this)
@@ -150,7 +145,6 @@ public void onResult(int resultCode, Intent intent) {
@Override
public void onPageChanged(int page, int pageCount) {
- pageNumber = page;
setTitle(String.format("%s %s / %s", pdfFileName, page + 1, pageCount));
}