Merge "Print spooler UI polish and bug fixes." into lmp-dev
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 9361286..bf8ac65 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -535,7 +535,7 @@
                 destroyed = isDestroyedLocked();
             }
 
-            if (destroyed) {
+            if (destroyed && observer != null) {
                 try {
                     observer.onDestroy();
                 } catch (RemoteException re) {
diff --git a/packages/PrintSpooler/res/layout/preview_page_loading.xml b/packages/PrintSpooler/res/layout/preview_page_loading.xml
new file mode 100644
index 0000000..1af3a17
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/preview_page_loading.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="36dip"
+        android:layout_gravity="center"
+        android:src="@drawable/ic_grayedout_printer"
+        android:contentDescription="@null"
+        android:scaleType="centerInside"
+        android:adjustViewBounds="true">
+    </ImageView>
+
+</FrameLayout>
diff --git a/packages/PrintSpooler/res/values-land/constants.xml b/packages/PrintSpooler/res/values-land/constants.xml
index 6cf9754b5..84fc050 100644
--- a/packages/PrintSpooler/res/values-land/constants.xml
+++ b/packages/PrintSpooler/res/values-land/constants.xml
@@ -16,7 +16,7 @@
 
 <resources>
 
-    <integer name="preview_page_per_row_count">2</integer>
+    <integer name="preview_page_per_row_count">4</integer>
 
     <integer name="print_option_column_count">3</integer>
 
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 5b7fda3..27e1d51 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -76,7 +76,6 @@
     <!-- Title for the print dialog announced to the user for accessibility. Not shown in the UI. [CHAR LIMIT=none] -->
     <string name="print_dialog">Print dialog</string>
 
-
     <!-- Template for the message that shows the current page out of the total number of pages -->
     <string name="current_page_template"><xliff:g id="current_page">%1$d</xliff:g>
         /<xliff:g id="page_count">%2$d</xliff:g></string>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index 09e8b39..1e7a011 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -317,6 +317,11 @@
         return mState == STATE_FAILED;
     }
 
+    public boolean hasLaidOutPages() {
+        return mDocumentInfo.info != null
+                && mDocumentInfo.info.getPageCount() > 0;
+    }
+
     public void clearUpdateError() {
         if (!hasUpdateError()) {
             throw new IllegalStateException("No update error to clear");
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index d949673..ce0b9b6 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -17,6 +17,9 @@
 package com.android.printspooler.ui;
 
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
 import android.os.ParcelFileDescriptor;
 import android.print.PageRange;
 import android.print.PrintAttributes.MediaSize;
@@ -26,12 +29,12 @@
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.View.MeasureSpec;
 import android.widget.TextView;
 import com.android.printspooler.R;
 import com.android.printspooler.model.PageContentRepository;
@@ -48,7 +51,7 @@
  * This class represents the adapter for the pages in the print preview list.
  */
 public final class PageAdapter extends Adapter implements
-        PageContentRepository.OnMalformedPdfFileListener{
+        PageContentRepository.OnMalformedPdfFileListener {
     private static final String LOG_TAG = "PageAdapter";
 
     private static final int MAX_PREVIEW_PAGES_BATCH = 50;
@@ -86,6 +89,8 @@
     // Pages the user selected in the UI.
     private PageRange[] mSelectedPages;
 
+    private BitmapDrawable mEmptyState;
+
     private int mDocumentPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
     private int mSelectedPageCount;
 
@@ -257,7 +262,7 @@
         }
 
         if (updatePreviewAreaAndPageSize) {
-            updatePreviewAreaAndPageSize();
+            updatePreviewAreaPageSizeAndEmptyState();
         }
 
         if (documentChanged) {
@@ -318,12 +323,12 @@
             }
 
             // OK, there are bugs in recycler view which tries to bind views
-            // without recycling them which would give us a chane to clean up.
+            // without recycling them which would give us a chance to clean up.
             PageContentProvider boundProvider = mPageContentRepository
-                   .peekPageContentProvider(pageIndexInFile);
+                    .peekPageContentProvider(pageIndexInFile);
             if (boundProvider != null) {
                 PageContentView owner = (PageContentView) boundProvider.getOwner();
-                owner.init(null, mMediaSize, mMinMargins);
+                owner.init(null, mEmptyState, mMediaSize, mMinMargins);
                 mPageContentRepository.releasePageContentProvider(boundProvider);
             }
 
@@ -333,7 +338,7 @@
         } else {
             onSelectedPageNotInFile(pageInDocument);
         }
-        content.init(provider, mMediaSize, mMinMargins);
+        content.init(provider, mEmptyState, mMediaSize, mMinMargins);
 
         View pageSelector = page.findViewById(R.id.page_selector);
         pageSelector.setTag(myHolder);
@@ -384,7 +389,7 @@
             mSelectedPages = selectedPages;
             mSelectedPageCount = PageRangeUtils.getNormalizedPageCount(
                     mSelectedPages, mDocumentPageCount);
-            updatePreviewAreaAndPageSize();
+            updatePreviewAreaPageSizeAndEmptyState();
             notifyDataSetChanged();
         }
         return mSelectedPages;
@@ -392,12 +397,12 @@
 
     public void onPreviewAreaSizeChanged() {
         if (mMediaSize != null) {
-            updatePreviewAreaAndPageSize();
+            updatePreviewAreaPageSizeAndEmptyState();
             notifyDataSetChanged();
         }
     }
 
-    private void updatePreviewAreaAndPageSize() {
+    private void updatePreviewAreaPageSizeAndEmptyState() {
         final int availableWidth = mPreviewArea.getWidth();
         final int availableHeight = mPreviewArea.getHeight();
 
@@ -419,7 +424,7 @@
         final int pageContentDesiredHeight = (int) (((float) pageContentDesiredWidth
                 / pageAspectRatio) + 0.5f);
 
-        // If the page does not fit entirely in a vertial direction,
+        // If the page does not fit entirely in a vertical direction,
         // we shirk it but not less than the minimal page width.
         final int pageContentMinHeight = (int) (mPreviewPageMinWidth / pageAspectRatio + 0.5f);
         final int pageContentMaxHeight = Math.max(pageContentMinHeight,
@@ -448,6 +453,23 @@
 
         mPreviewArea.setPadding(horizontalPadding, verticalPadding,
                 horizontalPadding, verticalPadding);
+
+        // 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),
+                MeasureSpec.makeMeasureSpec(mPageContentHeight, MeasureSpec.EXACTLY));
+        content.layout(0, 0, content.getMeasuredWidth(), content.getMeasuredHeight());
+
+        Bitmap bitmap = Bitmap.createBitmap(mPageContentWidth, mPageContentHeight,
+                Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+        content.draw(canvas);
+
+        // 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);
     }
 
     private PageRange[] computeSelectedPages() {
@@ -718,7 +740,7 @@
     private void recyclePageView(PageContentView page, int pageIndexInAdapter) {
         PageContentProvider provider = page.getPageContentProvider();
         if (provider != null) {
-            page.init(null, null, null);
+            page.init(null, null, null, null);
             mPageContentRepository.releasePageContentProvider(provider);
             mBoundPagesInAdapter.remove(pageIndexInAdapter);
         }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 022e0d0..535081f 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -114,14 +114,15 @@
     private static final int DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF = Integer.MAX_VALUE;
     private static final int DEST_ADAPTER_ITEM_ID_ALL_PRINTERS = Integer.MAX_VALUE - 1;
 
-    private static final int STATE_CONFIGURING = 0;
-    private static final int STATE_PRINT_CONFIRMED = 1;
-    private static final int STATE_PRINT_CANCELED = 2;
-    private static final int STATE_UPDATE_FAILED = 3;
-    private static final int STATE_CREATE_FILE_FAILED = 4;
-    private static final int STATE_PRINTER_UNAVAILABLE = 5;
-    private static final int STATE_UPDATE_SLOW = 6;
-    private static final int STATE_PRINT_COMPLETED = 7;
+    private static final int STATE_INITIALIZING = 0;
+    private static final int STATE_CONFIGURING = 1;
+    private static final int STATE_PRINT_CONFIRMED = 2;
+    private static final int STATE_PRINT_CANCELED = 3;
+    private static final int STATE_UPDATE_FAILED = 4;
+    private static final int STATE_CREATE_FILE_FAILED = 5;
+    private static final int STATE_PRINTER_UNAVAILABLE = 6;
+    private static final int STATE_UPDATE_SLOW = 7;
+    private static final int STATE_PRINT_COMPLETED = 8;
 
     private static final int UI_STATE_PREVIEW = 0;
     private static final int UI_STATE_ERROR = 1;
@@ -196,7 +197,7 @@
 
     private int mCurrentPageCount;
 
-    private int mState;
+    private int mState = STATE_INITIALIZING;
 
     private int mUiState = UI_STATE_PREVIEW;
 
@@ -290,10 +291,17 @@
         mPrintedDocument.start();
 
         ensurePreviewUiShown();
+
+        setState(STATE_CONFIGURING);
     }
 
     @Override
     public void onPause() {
+        if (mState == STATE_INITIALIZING) {
+            super.onPause();
+            return;
+        }
+
         if (isFinishing()) {
             PrintSpoolerService spooler = mSpoolerProvider.getSpooler();
             spooler.updatePrintJobUserConfigurableOptionsNoPersistence(mPrintJob);
@@ -341,9 +349,13 @@
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (mState == STATE_INITIALIZING) {
+            return super.onKeyUp(keyCode, event);
+        }
+
         if (keyCode == KeyEvent.KEYCODE_BACK
                 && event.isTracking() && !event.isCanceled()) {
-            if (mPrintPreviewController != null&&mPrintPreviewController.isOptionsOpened()
+            if (mPrintPreviewController != null && mPrintPreviewController.isOptionsOpened()
                     && !hasErrors()) {
                 mPrintPreviewController.closeOptions();
             } else {
@@ -900,7 +912,7 @@
         final boolean willUpdate = mPrintedDocument.update(mPrintJob.getAttributes(),
                 pages, preview);
 
-        if (willUpdate) {
+        if (willUpdate && !mPrintedDocument.hasLaidOutPages()) {
             // When the update is done we update the print preview.
             mProgressMessageController.post();
             return true;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
index 2b5b41b..a25e05e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
@@ -195,6 +195,7 @@
         if (mPageAdapter.isOpened()) {
             mPageAdapter.close(null);
         }
+        mRecyclerView.setAdapter(null);
         mPageAdapter.destroy();
     }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
index 23a01bd..e2ae758 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PageContentView.java
@@ -17,17 +17,15 @@
 package com.android.printspooler.widget;
 
 import android.content.Context;
-import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.print.PrintAttributes.MediaSize;
 import android.print.PrintAttributes.Margins;
 import android.util.AttributeSet;
-import android.util.TypedValue;
 import android.view.View;
 import com.android.printspooler.model.PageContentRepository;
-import com.android.printspooler.model.PageContentRepository.PageContentProvider;
 import com.android.printspooler.model.PageContentRepository.RenderSpec;
+import com.android.printspooler.model.PageContentRepository.PageContentProvider;
 
 /**
  * This class represents a page in the print preview list. The width of the page
@@ -37,35 +35,20 @@
  */
 public class PageContentView extends View
         implements PageContentRepository.OnPageContentAvailableCallback {
-
-    private final ColorDrawable mEmptyState;
-
     private PageContentProvider mProvider;
 
     private MediaSize mMediaSize;
 
     private Margins mMinMargins;
 
+    private Drawable mEmptyState;
+
     private boolean mContentRequested;
 
     private boolean mNeedsLayout;
 
     public PageContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
-
-        TypedValue typedValue = new TypedValue();
-        context.getTheme().resolveAttribute(com.android.internal.R.attr.textColorPrimary,
-                typedValue, true);
-
-        mEmptyState = new ColorDrawable(typedValue.data);
-
-        setBackground(mEmptyState);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-        requestPageContentIfNeeded();
     }
 
     @Override
@@ -85,15 +68,19 @@
         return mProvider;
     }
 
-    public void init(PageContentProvider provider, MediaSize mediaSize, Margins minMargins) {
+    public void init(PageContentProvider provider, Drawable emptyState,
+            MediaSize mediaSize, Margins minMargins) {
         final boolean providerChanged = (mProvider == null)
                 ? provider != null : !mProvider.equals(provider);
+        final boolean loadingDrawableChanged = (mEmptyState == null)
+                ? mEmptyState != null : !mEmptyState.equals(emptyState);
         final boolean mediaSizeChanged = (mMediaSize == null)
                 ? mediaSize != null : !mMediaSize.equals(mediaSize);
         final boolean marginsChanged = (mMinMargins == null)
                 ? minMargins != null : !mMinMargins.equals(minMargins);
 
-        if (!providerChanged && !mediaSizeChanged && !marginsChanged) {
+        if (!providerChanged && !mediaSizeChanged
+                && !marginsChanged && !loadingDrawableChanged) {
             return;
         }
 
@@ -101,6 +88,7 @@
         mMediaSize = mediaSize;
         mMinMargins = minMargins;
 
+        mEmptyState = emptyState;
         mContentRequested = false;
         mNeedsLayout = mNeedsLayout || mediaSizeChanged || marginsChanged;