summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Philip P. Moltmann <moltmann@google.com> 2016-03-23 17:17:39 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2016-03-23 17:17:40 +0000
commit49435a72955fd6d2673ac33c34e2417c96fd87fb (patch)
treea173dc392b612a41e50df2bb29d1c9c18387629b
parent8aa976b60b0b638a77f9007fa6d1a247815d4b09 (diff)
parent066bf81b983ce23b91d19b85b7c37a61fba7a9a6 (diff)
Merge "Deal with print-preview renderings that do not match the correct number of pages." into nyc-dev
-rw-r--r--packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp11
-rw-r--r--packages/PrintSpooler/res/drawable/print_warning.xml25
-rw-r--r--packages/PrintSpooler/res/layout/preview_page_error.xml39
-rw-r--r--packages/PrintSpooler/res/values/strings.xml4
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java49
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java89
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java35
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java23
8 files changed, 196 insertions, 79 deletions
diff --git a/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp b/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp
index 1530a02c22fe..1ce3949bbb81 100644
--- a/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp
+++ b/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp
@@ -50,6 +50,10 @@ static bool readAllBytes(const int fd, void* buffer, const size_t byteCount) {
size_t remainingBytes = byteCount;
while (remainingBytes > 0) {
ssize_t readByteCount = read(fd, readBuffer, remainingBytes);
+
+ remainingBytes -= readByteCount;
+ readBuffer += readByteCount;
+
if (readByteCount == -1) {
if (errno == EINTR) {
continue;
@@ -57,9 +61,12 @@ static bool readAllBytes(const int fd, void* buffer, const size_t byteCount) {
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
"Error reading from buffer: %d", errno);
return false;
+ } else if (readByteCount == 0 && remainingBytes > 0) {
+ __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
+ "File closed before all bytes were read. %zu/%zu remaining", remainingBytes,
+ byteCount);
+ return false;
}
- remainingBytes -= readByteCount;
- readBuffer += readByteCount;
}
return true;
}
diff --git a/packages/PrintSpooler/res/drawable/print_warning.xml b/packages/PrintSpooler/res/drawable/print_warning.xml
new file mode 100644
index 000000000000..35f0fed78a7f
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable/print_warning.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="96dp"
+ android:height="96dp"
+ android:viewportWidth="96.0"
+ android:viewportHeight="96.0">
+ <path
+ android:fillColor="#C8CCCE"
+ android:pathData="M4,84H92L48,8 4,84zM52,72h-8v-8h8v8zM52,56H44V40h8v16z"/>
+</vector>
diff --git a/packages/PrintSpooler/res/layout/preview_page_error.xml b/packages/PrintSpooler/res/layout/preview_page_error.xml
new file mode 100644
index 000000000000..4e9fb7787010
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/preview_page_error.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dip"
+ android:src="@drawable/print_warning"
+ android:contentDescription="@null" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip"
+ android:layout_marginEnd="16dip"
+ android:gravity="center_horizontal"
+ android:textColor="@android:color/black"
+ android:text="@string/print_cannot_load_page" />
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 4b566221e4fa..454c99d6a441 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -288,6 +288,10 @@
<!-- Message for the currently selected printer being unavailable. [CHAR LIMIT=100] -->
<string name="print_error_printer_unavailable">This printer isn\'t available right now.</string>
+ <!-- Message for the case when a preview of a page cannot be loaded because the printing app
+ provided a broken print preview rendering for this page. [CHAR LIMIT=50] -->
+ <string name="print_cannot_load_page">Can\'t display preview</string>
+
<!-- Long running operations -->
<!-- Message long running operation when preparing print preview. [CHAR LIMIT=50] -->
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index f8b134300908..bb359176bdf1 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -75,7 +75,7 @@ public final class PageContentRepository {
private int mState;
public interface OnPageContentAvailableCallback {
- public void onPageContentAvailable(BitmapDrawable content);
+ void onPageContentAvailable(BitmapDrawable content);
}
public PageContentRepository(Context context) {
@@ -741,6 +741,7 @@ public final class PageContentRepository {
final RenderSpec mRenderSpec;
OnPageContentAvailableCallback mCallback;
RenderedPage mRenderedPage;
+ private boolean mIsFailed;
public RenderPageTask(int pageIndex, RenderSpec renderSpec,
OnPageContentAvailableCallback callback) {
@@ -826,25 +827,24 @@ public final class PageContentRepository {
Bitmap bitmap = mRenderedPage.content.getBitmap();
- ParcelFileDescriptor[] pipe = null;
+ ParcelFileDescriptor[] pipe;
try {
pipe = ParcelFileDescriptor.createPipe();
- ParcelFileDescriptor source = pipe[0];
- ParcelFileDescriptor destination = pipe[1];
-
- mRenderer.renderPage(mPageIndex, bitmap.getWidth(), bitmap.getHeight(),
- mRenderSpec.printAttributes, destination);
-
- // We passed the file descriptor to the other side which took
- // ownership, so close our copy for the write to complete.
- destination.close();
-
- BitmapSerializeUtils.readBitmapPixels(bitmap, source);
- } catch (IOException|RemoteException e) {
- Log.e(LOG_TAG, "Error rendering page:" + mPageIndex, e);
- } finally {
- IoUtils.closeQuietly(pipe[0]);
- IoUtils.closeQuietly(pipe[1]);
+
+ try (ParcelFileDescriptor source = pipe[0]) {
+ try (ParcelFileDescriptor destination = pipe[1]) {
+
+ mRenderer.renderPage(mPageIndex, bitmap.getWidth(), bitmap.getHeight(),
+ mRenderSpec.printAttributes, destination);
+ }
+
+ BitmapSerializeUtils.readBitmapPixels(bitmap, source);
+ }
+
+ mIsFailed = false;
+ } catch (IOException|RemoteException|IllegalStateException e) {
+ Log.e(LOG_TAG, "Error rendering page " + mPageIndex, e);
+ mIsFailed = true;
}
return mRenderedPage;
@@ -859,15 +859,22 @@ public final class PageContentRepository {
// This task is done.
mPageToRenderTaskMap.remove(mPageIndex);
- // Take a note that the content is rendered.
- renderedPage.state = RenderedPage.STATE_RENDERED;
+ if (mIsFailed) {
+ renderedPage.state = RenderedPage.STATE_SCRAP;
+ } else {
+ renderedPage.state = RenderedPage.STATE_RENDERED;
+ }
// Invalidate all caches of the old state of the bitmap
mRenderedPage.content.invalidateSelf();
// Announce success if needed.
if (mCallback != null) {
- mCallback.onPageContentAvailable(renderedPage.content);
+ if (mIsFailed) {
+ mCallback.onPageContentAvailable(null);
+ } else {
+ mCallback.onPageContentAvailable(renderedPage.content);
+ }
}
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java
index 7db207498775..af4c34795917 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/renderer/PdfManipulationService.java
@@ -108,62 +108,65 @@ public final class PdfManipulationService extends Service {
try {
throwIfNotOpened();
- PdfRenderer.Page page = mRenderer.openPage(pageIndex);
+ try (PdfRenderer.Page page = mRenderer.openPage(pageIndex)) {
+ final int srcWidthPts = page.getWidth();
+ final int srcHeightPts = page.getHeight();
- final int srcWidthPts = page.getWidth();
- final int srcHeightPts = page.getHeight();
+ final int dstWidthPts = pointsFromMils(
+ attributes.getMediaSize().getWidthMils());
+ final int dstHeightPts = pointsFromMils(
+ attributes.getMediaSize().getHeightMils());
- final int dstWidthPts = pointsFromMils(
- attributes.getMediaSize().getWidthMils());
- final int dstHeightPts = pointsFromMils(
- attributes.getMediaSize().getHeightMils());
+ final boolean scaleContent = mRenderer.shouldScaleForPrinting();
+ final boolean contentLandscape = !attributes.getMediaSize().isPortrait();
- final boolean scaleContent = mRenderer.shouldScaleForPrinting();
- final boolean contentLandscape = !attributes.getMediaSize().isPortrait();
+ final float displayScale;
+ Matrix matrix = new Matrix();
- final float displayScale;
- Matrix matrix = new Matrix();
-
- if (scaleContent) {
- displayScale = Math.min((float) bitmapWidth / srcWidthPts,
- (float) bitmapHeight / srcHeightPts);
- } else {
- if (contentLandscape) {
- displayScale = (float) bitmapHeight / dstHeightPts;
+ if (scaleContent) {
+ displayScale = Math.min((float) bitmapWidth / srcWidthPts,
+ (float) bitmapHeight / srcHeightPts);
} else {
- displayScale = (float) bitmapWidth / dstWidthPts;
+ if (contentLandscape) {
+ displayScale = (float) bitmapHeight / dstHeightPts;
+ } else {
+ displayScale = (float) bitmapWidth / dstWidthPts;
+ }
}
- }
- matrix.postScale(displayScale, displayScale);
+ matrix.postScale(displayScale, displayScale);
- Configuration configuration = PdfManipulationService.this.getResources()
- .getConfiguration();
- if (configuration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
- matrix.postTranslate(bitmapWidth - srcWidthPts * displayScale, 0);
- }
+ Configuration configuration = PdfManipulationService.this.getResources()
+ .getConfiguration();
+ if (configuration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ matrix.postTranslate(bitmapWidth - srcWidthPts * displayScale, 0);
+ }
- Margins minMargins = attributes.getMinMargins();
- final int paddingLeftPts = pointsFromMils(minMargins.getLeftMils());
- final int paddingTopPts = pointsFromMils(minMargins.getTopMils());
- final int paddingRightPts = pointsFromMils(minMargins.getRightMils());
- final int paddingBottomPts = pointsFromMils(minMargins.getBottomMils());
+ Margins minMargins = attributes.getMinMargins();
+ final int paddingLeftPts = pointsFromMils(minMargins.getLeftMils());
+ final int paddingTopPts = pointsFromMils(minMargins.getTopMils());
+ final int paddingRightPts = pointsFromMils(minMargins.getRightMils());
+ final int paddingBottomPts = pointsFromMils(minMargins.getBottomMils());
- Rect clip = new Rect();
- clip.left = (int) (paddingLeftPts * displayScale);
- clip.top = (int) (paddingTopPts * displayScale);
- clip.right = (int) (bitmapWidth - paddingRightPts * displayScale);
- clip.bottom = (int) (bitmapHeight - paddingBottomPts * displayScale);
+ Rect clip = new Rect();
+ clip.left = (int) (paddingLeftPts * displayScale);
+ clip.top = (int) (paddingTopPts * displayScale);
+ clip.right = (int) (bitmapWidth - paddingRightPts * displayScale);
+ clip.bottom = (int) (bitmapHeight - paddingBottomPts * displayScale);
- if (DEBUG) {
- Log.i(LOG_TAG, "Rendering page:" + pageIndex);
- }
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Rendering page:" + pageIndex);
+ }
- Bitmap bitmap = getBitmapForSize(bitmapWidth, bitmapHeight);
- page.render(bitmap, clip, matrix, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
+ Bitmap bitmap = getBitmapForSize(bitmapWidth, bitmapHeight);
+ page.render(bitmap, clip, matrix, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
- page.close();
+ BitmapSerializeUtils.writeBitmapPixels(bitmap, destination);
+ }
+ } catch (Throwable e) {
+ Log.e(LOG_TAG, "Cannot render page", e);
- BitmapSerializeUtils.writeBitmapPixels(bitmap, destination);
+ // The error is propagated to the caller when it tries to read the bitmap and
+ // the pipe is closed prematurely
} finally {
IoUtils.closeQuietly(destination);
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index 645e182f0a1d..eebb60ce62e0 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -93,6 +93,7 @@ public final class PageAdapter extends Adapter<ViewHolder> {
private PageRange[] mSelectedPages;
private BitmapDrawable mEmptyState;
+ private BitmapDrawable mErrorState;
private int mDocumentPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
private int mSelectedPageCount;
@@ -329,7 +330,7 @@ public final class PageAdapter extends Adapter<ViewHolder> {
} else {
onSelectedPageNotInFile(pageInDocument);
}
- content.init(provider, mEmptyState, mMediaSize, mMinMargins);
+ content.init(provider, mEmptyState, mErrorState, mMediaSize, mMinMargins);
if (mConfirmedPagesInDocument.indexOfKey(pageInDocument) >= 0) {
page.setSelected(true, false);
@@ -448,19 +449,35 @@ public final class PageAdapter extends Adapter<ViewHolder> {
// Now update the empty state drawable, as it depends on the page
// size and is reused for all views for better performance.
LayoutInflater inflater = LayoutInflater.from(mContext);
- View content = inflater.inflate(R.layout.preview_page_loading, null, false);
- content.measure(MeasureSpec.makeMeasureSpec(mPageContentWidth, MeasureSpec.EXACTLY),
+ View loadingContent = inflater.inflate(R.layout.preview_page_loading, null, false);
+ loadingContent.measure(MeasureSpec.makeMeasureSpec(mPageContentWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mPageContentHeight, MeasureSpec.EXACTLY));
- content.layout(0, 0, content.getMeasuredWidth(), content.getMeasuredHeight());
+ loadingContent.layout(0, 0, loadingContent.getMeasuredWidth(),
+ loadingContent.getMeasuredHeight());
- Bitmap bitmap = Bitmap.createBitmap(mPageContentWidth, mPageContentHeight,
+ Bitmap loadingBitmap = Bitmap.createBitmap(mPageContentWidth, mPageContentHeight,
Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- content.draw(canvas);
+ loadingContent.draw(new Canvas(loadingBitmap));
// Do not recycle the old bitmap if such as it may be set as an empty
// state to any of the page views. Just let the GC take care of it.
- mEmptyState = new BitmapDrawable(mContext.getResources(), bitmap);
+ mEmptyState = new BitmapDrawable(mContext.getResources(), loadingBitmap);
+
+ // Now update the empty state drawable, as it depends on the page
+ // size and is reused for all views for better performance.
+ View errorContent = inflater.inflate(R.layout.preview_page_error, null, false);
+ errorContent.measure(MeasureSpec.makeMeasureSpec(mPageContentWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(mPageContentHeight, MeasureSpec.EXACTLY));
+ errorContent.layout(0, 0, errorContent.getMeasuredWidth(),
+ errorContent.getMeasuredHeight());
+
+ Bitmap errorBitmap = Bitmap.createBitmap(mPageContentWidth, mPageContentHeight,
+ Bitmap.Config.ARGB_8888);
+ errorContent.draw(new Canvas(errorBitmap));
+
+ // Do not recycle the old bitmap if such as it may be set as an error
+ // state to any of the page views. Just let the GC take care of it.
+ mErrorState = new BitmapDrawable(mContext.getResources(), errorBitmap);
}
private PageRange[] computeSelectedPages() {
@@ -742,7 +759,7 @@ public final class PageAdapter extends Adapter<ViewHolder> {
private void recyclePageView(PageContentView page, int pageIndexInAdapter) {
PageContentProvider provider = page.getPageContentProvider();
if (provider != null) {
- page.init(null, mEmptyState, mMediaSize, mMinMargins);
+ page.init(null, mEmptyState, mErrorState, mMediaSize, mMinMargins);
mPageContentRepository.releasePageContentProvider(provider);
}
mBoundPagesInAdapter.remove(pageIndexInAdapter);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
index b79278950eb4..7ef42d149ee6 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
@@ -44,8 +44,12 @@ public class PageContentView extends View
private Drawable mEmptyState;
+ private Drawable mErrorState;
+
private boolean mContentRequested;
+ private boolean mIsFailed;
+
public PageContentView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -53,19 +57,26 @@ public class PageContentView extends View
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mContentRequested = false;
+
requestPageContentIfNeeded();
}
@Override
- public void onPageContentAvailable(BitmapDrawable content) {
- setBackground(content);
+ public void onPageContentAvailable(BitmapDrawable renderedPage) {
+ mIsFailed = (renderedPage == null);
+
+ if (mIsFailed) {
+ setBackground(mErrorState);
+ } else {
+ setBackground(renderedPage);
+ }
}
public PageContentProvider getPageContentProvider() {
return mProvider;
}
- public void init(PageContentProvider provider, Drawable emptyState,
+ public void init(PageContentProvider provider, Drawable emptyState, Drawable errorState,
MediaSize mediaSize, Margins minMargins) {
final boolean providerChanged = (mProvider == null)
? provider != null : !mProvider.equals(provider);
@@ -81,17 +92,21 @@ public class PageContentView extends View
return;
}
+ mIsFailed = false;
mProvider = provider;
mMediaSize = mediaSize;
mMinMargins = minMargins;
mEmptyState = emptyState;
+ mErrorState = errorState;
mContentRequested = false;
// If there is no provider we want immediately to switch to
// the empty state, so pages with no content appear blank.
- if (mProvider == null && getBackground() != mEmptyState) {
+ if (mProvider == null) {
setBackground(mEmptyState);
+ } else if (mIsFailed) {
+ setBackground(mErrorState);
}
requestPageContentIfNeeded();