am f22a767e: Merge "Added UI for errors during layout and write." into klp-dev

* commit 'f22a767e872ec8f5c2531a88819e0f4574c78fe3':
  Added UI for errors during layout and write.
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 7eea3e9..48564911 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -41,8 +41,6 @@
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"/>
 
-    <uses-sdk android:minSdkVersion="18" android:targetSdkVersion="18"/>
-
     <application
             android:allowClearUserData="true"
             android:label="@string/app_label"
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_content_error.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_content_error.xml
new file mode 100644
index 0000000..222b5b6
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity_content_error.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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:id="@+id/content_generating"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:background="@color/editable_background"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/message"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="16dip"
+            android:layout_marginEnd="16dip"
+            android:layout_marginTop="32dip"
+            android:layout_marginBottom="32dip"
+            android:layout_gravity="center"
+            style="?android:attr/buttonBarButtonStyle"
+            android:singleLine="true"
+            android:ellipsize="end"
+            android:text="@string/print_error_default_message"
+            android:textColor="@color/important_text"
+            android:textSize="16sp">
+        </TextView>
+
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="1dip"
+            android:background="@color/separator">
+        </View>
+
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/ok_button"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="fill_horizontal"
+        style="?android:attr/buttonBarButtonStyle"
+        android:text="@android:string/ok"
+        android:textSize="16sp"
+        android:textColor="@color/important_text">
+    </Button>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 67c455d..d74b414 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -44,7 +44,7 @@
     <string name="label_pages">Pages (<xliff:g id="page_count" example="5">%1$s</xliff:g>)</string>
 
     <!-- Page range exmple used as a hint of how to specify such. [CHAR LIMIT=20] -->
-    <string name="pages_range_example">e.g. 1&#8211;5, 8, 11&#8211;13</string>
+    <string name="pages_range_example">e.g. 1&#8212;5,8,11&#8212;13</string>
 
     <!-- Title for the pring preview button .[CHAR LIMIT=30] -->
     <string name="print_preview">Print preview</string>
@@ -142,6 +142,9 @@
     <!-- Label for a printer that is not available. [CHAR LIMIT=25] -->
     <string name="printer_unavailable"><xliff:g id="print_job_name" example="Canon-123GHT">%1$s</xliff:g> &#8211; unavailable</string>
 
+    <!-- Default message of an alert dialog for app error while generating a print job. [CHAR LIMIT=50] -->
+    <string name="print_error_default_message">Couldn\'t generate print job</string>
+
     <!-- Arrays -->
 
     <!-- Color mode labels. -->
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index 007d9c0..3ba7369 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -89,6 +89,8 @@
 
 import libcore.io.IoUtils;
 
+import libcore.io.IoUtils;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -357,6 +359,10 @@
             return mControllerState >= CONTROLLER_STATE_LAYOUT_COMPLETED;
         }
 
+        public boolean isPerformingLayout() {
+            return mControllerState == CONTROLLER_STATE_LAYOUT_STARTED;
+        }
+
         public boolean isWorking() {
             return mControllerState == CONTROLLER_STATE_LAYOUT_STARTED
                     || mControllerState == CONTROLLER_STATE_WRITE_STARTED;
@@ -372,9 +378,18 @@
             if (!mController.hasStarted()) {
                 mController.start();
             }
+
+            // If the print attributes are the same and we are performing
+            // a layout, then we have to wait for it to completed which will
+            // trigger writing of the necessary pages.
+            final boolean printAttributesChanged = printAttributesChanged();
+            if (!printAttributesChanged && isPerformingLayout()) {
+                return;
+            }
+
             // If print is confirmed we always do a layout since the previous
             // ones were for preview and this one is for printing.
-            if (!printAttributesChanged() && !mEditor.isPrintConfirmed()) {
+            if (!printAttributesChanged && !mEditor.isPrintConfirmed()) {
                 if (mDocument.info == null) {
                     // We are waiting for the result of a layout, so do nothing.
                     return;
@@ -492,14 +507,20 @@
                     mRequestCounter.incrementAndGet());
         }
 
-        private void handleOnLayoutFailed(CharSequence error, int sequence) {
+        private void handleOnLayoutFailed(final CharSequence error, int sequence) {
             if (mRequestCounter.get() != sequence) {
                 return;
             }
             mControllerState = CONTROLLER_STATE_FAILED;
-            // TODO: We need some UI for announcing an error.
-            Log.e(LOG_TAG, "Error during layout: " + error);
-            PrintJobConfigActivity.this.finish();
+            mEditor.showUi(Editor.UI_ERROR, new Runnable() {
+                @Override
+                public void run() {
+                    if (!TextUtils.isEmpty(error)) {
+                        TextView messageView = (TextView) findViewById(R.id.message);
+                        messageView.setText(error);
+                    }
+                }
+            });
         }
 
         private void handleOnWriteFinished(PageRange[] pages, int sequence) {
@@ -596,13 +617,20 @@
             }
         }
 
-        private void handleOnWriteFailed(CharSequence error, int sequence) {
+        private void handleOnWriteFailed(final CharSequence error, int sequence) {
             if (mRequestCounter.get() != sequence) {
                 return;
             }
             mControllerState = CONTROLLER_STATE_FAILED;
-            Log.e(LOG_TAG, "Error during write: " + error);
-            PrintJobConfigActivity.this.finish();
+            mEditor.showUi(Editor.UI_ERROR, new Runnable() {
+                @Override
+                public void run() {
+                    if (!TextUtils.isEmpty(error)) {
+                        TextView messageView = (TextView) findViewById(R.id.message);
+                        messageView.setText(error);
+                    }
+                }
+            });
         }
 
         private boolean equalsIgnoreSize(PrintDocumentInfo lhs, PrintDocumentInfo rhs) {
@@ -800,6 +828,7 @@
         private static final int UI_NONE = 0;
         private static final int UI_EDITING_PRINT_JOB = 1;
         private static final int UI_GENERATING_PRINT_JOB = 2;
+        private static final int UI_ERROR = 3;
 
         private EditText mCopiesEditText;
 
@@ -1310,6 +1339,21 @@
             updateUi();
         }
 
+        public void reselectCurrentPrinter() {
+            if (mCurrentPrinter != null) {
+                // TODO: While the data did not change and we set the adapter
+                // to a newly inflated spinner, the latter does not show the
+                // current item unless we poke the adapter. This requires more
+                // investigation. Maybe an optimization in AdapterView does not
+                // call into the adapter if the view is not visible which is the
+                // case when we set the adapter.
+                mDestinationSpinnerAdapter.notifyDataSetChanged();
+                final int position = mDestinationSpinnerAdapter.getPrinterIndex(
+                        mCurrentPrinter.getId());
+                mDestinationSpinner.setSelection(position);
+            }
+        }
+
         public void refreshCurrentPrinter() {
             PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
             if (printer != null) {
@@ -1407,7 +1451,10 @@
                 return;
             }
 
-            switch (mCurrentUi) {
+            final int oldUi = mCurrentUi;
+            mCurrentUi = ui;
+
+            switch (oldUi) {
                 case UI_NONE: {
                     switch (ui) {
                         case UI_EDITING_PRINT_JOB: {
@@ -1444,6 +1491,21 @@
                             new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                                     ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
                         } break;
+
+                        case UI_ERROR: {
+                            animateUiSwitch(R.layout.print_job_config_activity_content_error,
+                                    new Runnable() {
+                                @Override
+                                public void run() {
+                                    registerOkButtonClickListener();
+                                    if (postSwitchCallback != null) {
+                                        postSwitchCallback.run();
+                                    }
+                                }
+                            },
+                            new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                                    ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
+                        } break;
                     }
                 } break;
 
@@ -1463,11 +1525,43 @@
                             new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                     ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER));
                         } break;
+
+                        case UI_ERROR: {
+                            animateUiSwitch(R.layout.print_job_config_activity_content_error,
+                                    new Runnable() {
+                                @Override
+                                public void run() {
+                                    registerOkButtonClickListener();
+                                    if (postSwitchCallback != null) {
+                                        postSwitchCallback.run();
+                                    }
+                                }
+                            },
+                            new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                                    ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
+                        } break;
+                    }
+                } break;
+
+                case UI_ERROR: {
+                    switch (ui) {
+                        case UI_EDITING_PRINT_JOB: {
+                            animateUiSwitch(R.layout.print_job_config_activity_content_editing,
+                                    new Runnable() {
+                                @Override
+                                public void run() {
+                                    registerPrintButtonClickListener();
+                                    if (postSwitchCallback != null) {
+                                        postSwitchCallback.run();
+                                    }
+                                }
+                            },
+                            new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                                    ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER));
+                        } break;
                     }
                 } break;
             }
-
-            mCurrentUi = ui;
         }
 
         private void registerPrintButtonClickListener() {
@@ -1503,13 +1597,33 @@
             });
         }
 
+        private void registerOkButtonClickListener() {
+            Button okButton = (Button) findViewById(R.id.ok_button);
+            okButton.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mEditor.showUi(Editor.UI_EDITING_PRINT_JOB, new Runnable() {
+                        @Override
+                        public void run() {
+                            // Start over with a clean slate.
+                            mOldPrintAttributes.clear();
+                            mController.initialize();
+                            mEditor.initialize();
+                            mEditor.bindUi();
+                            mEditor.reselectCurrentPrinter();
+                        }
+                    });
+                }
+            });
+        }
+
         private void doUiSwitch(int showLayoutId) {
             ViewGroup contentContainer = (ViewGroup) findViewById(R.id.content_container);
             contentContainer.removeAllViews();
             getLayoutInflater().inflate(showLayoutId, contentContainer, true);
         }
 
-        private void animateUiSwitch(int showLayoutId, final Runnable postAnimateCommand,
+        private void animateUiSwitch(int showLayoutId, final Runnable beforeShowNewUiAction,
                 final LayoutParams containerParams) {
             // Find everything we will shuffle around.
             final ViewGroup contentContainer = (ViewGroup) findViewById(R.id.content_container);
@@ -1538,7 +1652,7 @@
                             / (float) contentContainer.getHeight();
 
                     // Second animation - resize the container.
-                    AutoCancellingAnimator.animate(contentContainer).scaleY(scaleY).withLayer()
+                    AutoCancellingAnimator.animate(contentContainer).scaleY(scaleY)
                             .withEndAction(new Runnable() {
                         @Override
                         public void run() {
@@ -1549,14 +1663,10 @@
 
                             contentContainer.setLayoutParams(containerParams);
 
+                            beforeShowNewUiAction.run();
+
                             // Third animation - show the new content.
-                            AutoCancellingAnimator.animate(showingView).withLayer().alpha(1.0f)
-                                    .withEndAction(new Runnable() {
-                                @Override
-                                public void run() {
-                                    postAnimateCommand.run();
-                                }
-                            });
+                            AutoCancellingAnimator.animate(showingView).alpha(1.0f);
                         }
                     });
                 }