am 0298ebb2: am 60fb1e28: am 714ba345: Merge "Complete implementation of the advanced print options." into klp-dev

* commit '0298ebb2db5b7c24d68fd3817b11e2f887471d0c':
  Complete implementation of the advanced print options.
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index c2f190d..63f94fe 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -16,6 +16,7 @@
 
 package android.print;
 
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -160,6 +161,9 @@
     /** Information about the printed document. */
     private PrintDocumentInfo mDocumentInfo;
 
+    /** Advanced printer specific options. */
+    private Bundle mAdvancedOptions;
+
     /** Whether we are trying to cancel this print job. */
     private boolean mCanceling;
 
@@ -184,6 +188,7 @@
         mAttributes = other.mAttributes;
         mDocumentInfo = other.mDocumentInfo;
         mCanceling = other.mCanceling;
+        mAdvancedOptions = other.mAdvancedOptions;
     }
 
     private PrintJobInfo(Parcel parcel) {
@@ -197,20 +202,17 @@
         mCreationTime = parcel.readLong();
         mCopies = parcel.readInt();
         mStateReason = parcel.readString();
-        if (parcel.readInt() == 1) {
-            Parcelable[] parcelables = parcel.readParcelableArray(null);
+        Parcelable[] parcelables = parcel.readParcelableArray(null);
+        if (parcelables != null) {
             mPageRanges = new PageRange[parcelables.length];
             for (int i = 0; i < parcelables.length; i++) {
                 mPageRanges[i] = (PageRange) parcelables[i];
             }
         }
-        if (parcel.readInt() == 1) {
-            mAttributes = PrintAttributes.CREATOR.createFromParcel(parcel);
-        }
-        if (parcel.readInt() == 1) {
-            mDocumentInfo = PrintDocumentInfo.CREATOR.createFromParcel(parcel);
-        }
+        mAttributes = (PrintAttributes) parcel.readParcelable(null);
+        mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null);
         mCanceling = (parcel.readInt() == 1);
+        mAdvancedOptions = parcel.readBundle();
     }
 
     /**
@@ -521,6 +523,71 @@
         mCanceling = cancelling;
     }
 
+    /**
+     * Gets whether this job has a given advanced (printer specific) print
+     * option.
+     *
+     * @param key The option key.
+     * @return Whether the option is present.
+     *
+     * @hide
+     */
+    public boolean hasAdvancedOption(String key) {
+        return mAdvancedOptions != null && mAdvancedOptions.containsKey(key);
+    }
+
+    /**
+     * Gets the value of an advanced (printer specific) print option.
+     *
+     * @param key The option key.
+     * @return The option value.
+     *
+     * @hide
+     */
+    public String getAdvancedStringOption(String key) {
+        if (mAdvancedOptions != null) {
+            return mAdvancedOptions.getString(key);
+        }
+        return null;
+    }
+
+    /**
+     * Gets the value of an advanced (printer specific) print option.
+     *
+     * @param key The option key.
+     * @return The option value.
+     *
+     * @hide
+     */
+    public int getAdvancedIntOption(String key) {
+        if (mAdvancedOptions != null) {
+            return mAdvancedOptions.getInt(key);
+        }
+        return 0;
+    }
+
+    /**
+     * Gets the advanced options.
+     *
+     * @return The advanced options.
+     *
+     * @hide
+     */
+    public Bundle getAdvancedOptions() {
+        return mAdvancedOptions;
+    }
+
+    /**
+     * Sets the advanced options.
+     *
+     * @param options The advanced options.
+     *
+     * @hide
+     */
+    public void setAdvancedOptions(Bundle options) {
+        mAdvancedOptions = options;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -538,25 +605,11 @@
         parcel.writeLong(mCreationTime);
         parcel.writeInt(mCopies);
         parcel.writeString(mStateReason);
-        if (mPageRanges != null) {
-            parcel.writeInt(1);
-            parcel.writeParcelableArray(mPageRanges, flags);
-        } else {
-            parcel.writeInt(0);
-        }
-        if (mAttributes != null) {
-            parcel.writeInt(1);
-            mAttributes.writeToParcel(parcel, flags);
-        } else {
-            parcel.writeInt(0);
-        }
-        if (mDocumentInfo != null) {
-            parcel.writeInt(1);
-            mDocumentInfo.writeToParcel(parcel, flags);
-        } else {
-            parcel.writeInt(0);
-        }
+        parcel.writeParcelableArray(mPageRanges, flags);
+        parcel.writeParcelable(mAttributes, flags);
+        parcel.writeParcelable(mDocumentInfo, 0);
         parcel.writeInt(mCanceling ? 1 : 0);
+        parcel.writeBundle(mAdvancedOptions);
     }
 
     @Override
@@ -577,6 +630,7 @@
         builder.append(", cancelling: " + mCanceling);
         builder.append(", pages: " + (mPageRanges != null
                 ? Arrays.toString(mPageRanges) : null));
+        builder.append(", hasAdvancedOptions: " + (mAdvancedOptions != null));
         builder.append("}");
         return builder.toString();
     }
@@ -663,7 +717,10 @@
          * @param value The option value.
          */
         public void putAdvancedOption(String key, String value) {
-
+            if (mPrototype.mAdvancedOptions == null) {
+                mPrototype.mAdvancedOptions = new Bundle();
+            }
+            mPrototype.mAdvancedOptions.putString(key, value);
         }
 
         /**
@@ -673,7 +730,10 @@
          * @param value The option value.
          */
         public void putAdvancedOption(String key, int value) {
-
+            if (mPrototype.mAdvancedOptions == null) {
+                mPrototype.mAdvancedOptions = new Bundle();
+            }
+            mPrototype.mAdvancedOptions.putInt(key, value);
         }
 
         /**
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index fdeb373..6fa0bdd 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -321,7 +321,7 @@
      */
     public String getAdvancedStringOption(String key) {
         PrintService.throwIfNotCalledOnMainThread();
-        return null;
+        return getInfo().getAdvancedStringOption(key);
     }
 
     /**
@@ -333,7 +333,7 @@
      */
     public boolean hasAdvancedOption(String key) {
         PrintService.throwIfNotCalledOnMainThread();
-        return false;
+        return getInfo().hasAdvancedOption(key);
     }
 
     /**
@@ -344,7 +344,7 @@
      */
     public int getAdvancedIntOption(String key) {
         PrintService.throwIfNotCalledOnMainThread();
-        return 0;
+        return getInfo().getAdvancedIntOption(key);
     }
 
     @Override
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 0fc5f7f..eb0ac2e 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -209,6 +209,14 @@
      * PrintJob#getAdvancedStringOption(String) PrintJob.getAdvancedStringOption(String)}
      * and {@link PrintJob#getAdvancedIntOption(String) PrintJob.getAdvancedIntOption(String)}.
      * </p>
+     * <p>
+     * If the advanced print options activity offers changes to the standard print
+     * options, you can get the current {@link android.print.PrinterInfo} using the
+     * "android.intent.extra.print.EXTRA_PRINTER_INFO" extra which will allow you to
+     * present the user with UI options supported by the current printer. For example,
+     * if the current printer does not support a give media size, you should not
+     * offer it in the advanced print options dialog.
+     * </p>
      */
     public static final String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO";
 
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
index 8e9636c..a2c6c09e 100644
--- a/core/java/android/printservice/PrintServiceInfo.java
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -60,6 +60,8 @@
 
     private final String mAddPrintersActivityName;
 
+    private final String mAdvancedPrintOptionsActivityName;
+
     /**
      * Creates a new instance.
      *
@@ -70,6 +72,7 @@
         mResolveInfo = parcel.readParcelable(null);
         mSettingsActivityName = parcel.readString();
         mAddPrintersActivityName = parcel.readString();
+        mAdvancedPrintOptionsActivityName = parcel.readString();
     }
 
     /**
@@ -78,14 +81,16 @@
      * @param resolveInfo The service resolve info.
      * @param settingsActivityName Optional settings activity name.
      * @param addPrintersActivityName Optional add printers activity name.
+     * @param advancedPrintOptionsActivityName Optional advanced print options activity.
      */
     public PrintServiceInfo(ResolveInfo resolveInfo, String settingsActivityName,
-            String addPrintersActivityName) {
+            String addPrintersActivityName, String advancedPrintOptionsActivityName) {
         mId = new ComponentName(resolveInfo.serviceInfo.packageName,
                 resolveInfo.serviceInfo.name).flattenToString();
         mResolveInfo = resolveInfo;
         mSettingsActivityName = settingsActivityName;
         mAddPrintersActivityName = addPrintersActivityName;
+        mAdvancedPrintOptionsActivityName = advancedPrintOptionsActivityName;
     }
 
     /**
@@ -99,6 +104,7 @@
     public static PrintServiceInfo create(ResolveInfo resolveInfo, Context context) {
         String settingsActivityName = null;
         String addPrintersActivityName = null;
+        String advancedPrintOptionsActivityName = null;
 
         XmlResourceParser parser = null;
         PackageManager packageManager = context.getPackageManager();
@@ -128,6 +134,9 @@
                     addPrintersActivityName = attributes.getString(
                             com.android.internal.R.styleable.PrintService_addPrintersActivity);
 
+                    advancedPrintOptionsActivityName = attributes.getString(com.android.internal
+                            .R.styleable.PrintService_advancedPrintOptionsActivity);
+
                     attributes.recycle();
                 }
             } catch (IOException ioe) {
@@ -144,7 +153,8 @@
             }
         }
 
-        return new PrintServiceInfo(resolveInfo, settingsActivityName, addPrintersActivityName);
+        return new PrintServiceInfo(resolveInfo, settingsActivityName,
+                addPrintersActivityName, advancedPrintOptionsActivityName);
     }
 
     /**
@@ -195,6 +205,19 @@
     }
 
     /**
+     * The advanced print options activity name.
+     * <p>
+     * <strong>Statically set from
+     * {@link PrintService#SERVICE_META_DATA meta-data}.</strong>
+     * </p>
+     *
+     * @return The advanced print options activity name.
+     */
+    public String getAdvancedOptionsActivityName() {
+        return mAdvancedPrintOptionsActivityName;
+    }
+
+    /**
      * {@inheritDoc}
      */
     public int describeContents() {
@@ -206,6 +229,7 @@
         parcel.writeParcelable(mResolveInfo, 0);
         parcel.writeString(mSettingsActivityName);
         parcel.writeString(mAddPrintersActivityName);
+        parcel.writeString(mAdvancedPrintOptionsActivityName);
     }
 
     @Override
@@ -243,6 +267,8 @@
         builder.append(", resolveInfo=").append(mResolveInfo);
         builder.append(", settingsActivityName=").append(mSettingsActivityName);
         builder.append(", addPrintersActivityName=").append(mAddPrintersActivityName);
+        builder.append(", advancedPrintOptionsActivityName=")
+                .append(mAdvancedPrintOptionsActivityName);
         builder.append("}");
         return builder.toString();
     }
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9dcefee..a337098 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2636,7 +2636,11 @@
              add printers to this print service. -->
         <attr name="addPrintersActivity" format="string"/>
         <!-- Fully qualified class name of an activity with advanced print options
-             specific to this print service. -->
+             specific to this print service. If this activity is specified the system
+             will allow the user a choice to open it given the currently selected printer
+             has advanced options which is specified by the print service via
+             {@link android.print.PrinterInfo.Builder#setHasAdvancedOptions(boolean)}.
+             -->
         <attr name="advancedPrintOptionsActivity" format="string"/>
         <!-- The vendor name if this print service is vendor specific. -->
         <attr name="vendor" format="string"/>
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
index d503216..3303ef1 100644
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
@@ -16,7 +16,7 @@
 
 <com.android.printspooler.PrintDialogFrame xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
-    android:layout_height="fill_parent">
+    android:layout_height="wrap_content">
     <FrameLayout
         android:id="@+id/content_container"
         android:layout_width="fill_parent"
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml
index 02740a3..e50a7af 100644
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml
@@ -18,8 +18,7 @@
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
-    android:scrollbars="vertical"
-    android:background="@color/editable_background">
+    android:scrollbars="vertical">
 
     <LinearLayout
         android:layout_width="fill_parent"
@@ -42,6 +41,7 @@
         <LinearLayout
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
+            android:layout_marginBottom="24dip"
             android:orientation="horizontal"
             android:baselineAligned="false">
 
@@ -203,27 +203,79 @@
 
         </LinearLayout>
 
+        <!-- Advanced settings button -->
+
+        <LinearLayout
+           android:id="@+id/advanced_settings_container"
+           android:layout_width="fill_parent"
+           android:layout_height="wrap_content"
+           android:orientation="vertical"
+           android:visibility="gone">
+
+            <ImageView
+                android:layout_width="fill_parent"
+                android:layout_height="1dip"
+                android:layout_marginStart="24dip"
+                android:layout_marginEnd="24dip"
+                android:layout_gravity="fill_horizontal"
+                android:background="@color/separator"
+                android:contentDescription="@null">
+            </ImageView>
+
+            <Button
+                android:id="@+id/advanced_settings_button"
+                style="?android:attr/buttonBarButtonStyle"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="24dip"
+                android:layout_marginEnd="24dip"
+                android:layout_gravity="fill_horizontal"
+                android:text="@string/advanced_settings_button"
+                android:gravity="start|center_vertical"
+                android:textSize="16sp"
+                android:textColor="@color/item_text_color">
+            </Button>
+
+            <ImageView
+                android:layout_width="fill_parent"
+                android:layout_height="1dip"
+                android:layout_gravity="fill_horizontal"
+                android:layout_marginStart="24dip"
+                android:layout_marginEnd="24dip"
+                android:background="@color/separator"
+                android:contentDescription="@null">
+            </ImageView>
+
+        </LinearLayout>
+
         <!-- Print button -->
 
-        <ImageView
-            android:layout_width="fill_parent"
-            android:layout_height="1dip"
-            android:layout_marginTop="24dip"
-            android:layout_gravity="fill_horizontal"
-            android:background="@color/separator"
-            android:contentDescription="@null">
-        </ImageView>
-
-        <Button
-            android:id="@+id/print_button"
-            style="?android:attr/buttonBarButtonStyle"
+        <FrameLayout
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
-            android:layout_gravity="fill_horizontal"
-            android:text="@string/print_button"
-            android:textSize="16sp"
-            android:textColor="@color/item_text_color">
-        </Button>
+            android:layout_marginTop="24dip"
+            android:background="@color/action_button_background">
+
+            <ImageView
+                android:layout_width="fill_parent"
+                android:layout_height="1dip"
+                android:layout_gravity="fill_horizontal"
+                android:background="@color/separator"
+                android:contentDescription="@null">
+            </ImageView>
+
+            <Button
+                android:id="@+id/print_button"
+                style="?android:attr/buttonBarButtonStyle"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="fill_horizontal"
+                android:text="@string/print_button"
+                android:textSize="16sp"
+                android:textColor="@color/item_text_color">
+            </Button>
+
+        </FrameLayout>
 
     </LinearLayout>
 
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
index 222b5b6..f573d9d 100644
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_content_error.xml
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity_content_error.xml
@@ -23,7 +23,6 @@
     <LinearLayout
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
-        android:background="@color/editable_background"
         android:orientation="vertical">
 
         <TextView
@@ -43,23 +42,30 @@
             android:textSize="16sp">
         </TextView>
 
+    </LinearLayout>
+
+    <FrameLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:background="@color/action_button_background">
+
         <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>
 
-    <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>
+    </FrameLayout>
 
 </LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_content_generating.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_content_generating.xml
index 8bdb6c9..10602ee 100644
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_content_generating.xml
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity_content_generating.xml
@@ -23,7 +23,6 @@
     <LinearLayout
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
-        android:background="@color/editable_background"
         android:orientation="vertical">
 
         <TextView
@@ -51,23 +50,30 @@
             style="?android:attr/progressBarStyleLarge">
         </ProgressBar>
 
+    </LinearLayout>
+
+    <FrameLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:background="@color/action_button_background">
+
         <View
             android:layout_width="fill_parent"
             android:layout_height="1dip"
             android:background="@color/separator">
         </View>
 
-    </LinearLayout>
+        <Button
+            android:id="@+id/cancel_button"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="fill_horizontal"
+            style="?android:attr/buttonBarButtonStyle"
+            android:text="@string/cancel"
+            android:textSize="16sp"
+            android:textColor="@color/important_text">
+        </Button>
 
-    <Button
-        android:id="@+id/cancel_button"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="fill_horizontal"
-        style="?android:attr/buttonBarButtonStyle"
-        android:text="@string/cancel"
-        android:textSize="16sp"
-        android:textColor="@color/important_text">
-    </Button>
+    </FrameLayout>
 
 </LinearLayout>
diff --git a/packages/PrintSpooler/res/values/colors.xml b/packages/PrintSpooler/res/values/colors.xml
index 9972c96..4fc25b3 100644
--- a/packages/PrintSpooler/res/values/colors.xml
+++ b/packages/PrintSpooler/res/values/colors.xml
@@ -16,10 +16,10 @@
 
 <resources>
 
-    <color name="container_background">#FFFFFF</color>
+    <color name="container_background">#F2F2F2</color>
     <color name="important_text">#333333</color>
     <color name="print_option_title">#888888</color>
     <color name="separator">#CCCCCC</color>
-    <color name="editable_background">#F2F2F2</color>
+    <color name="action_button_background">#FFFFFF</color>
 
-</resources>
\ No newline at end of file
+</resources>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index d74b414..f2e768a 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -19,6 +19,9 @@
     <!-- Title of the PrintSpooler application. [CHAR LIMIT=50] -->
     <string name="app_label">Print Spooler</string>
 
+    <!-- Label of the print dialog's button for advanced printer settings. [CHAR LIMIT=25] -->
+    <string name="advanced_settings_button">Printer settings</string>
+
     <!-- Label of the print dialog's print button. [CHAR LIMIT=16] -->
     <string name="print_button">Print</string>
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index db3770e..8f26361 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.app.Dialog;
 import android.app.LoaderManager;
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -26,6 +27,8 @@
 import android.content.ServiceConnection;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.database.DataSetObserver;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -55,6 +58,8 @@
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
+import android.printservice.PrintService;
+import android.printservice.PrintServiceInfo;
 import android.provider.DocumentsContract;
 import android.text.Editable;
 import android.text.TextUtils;
@@ -130,6 +135,7 @@
 
     private static final int ACTIVITY_REQUEST_CREATE_FILE = 1;
     private static final int ACTIVITY_REQUEST_SELECT_PRINTER = 2;
+    private static final int ACTIVITY_POPULATE_ADVANCED_PRINT_OPTIONS = 3;
 
     private static final int CONTROLLER_STATE_FINISHED = 1;
     private static final int CONTROLLER_STATE_FAILED = 2;
@@ -254,28 +260,27 @@
     }
 
     @Override
-    protected void onDestroy() {
-        // We can safely do the work in here since at this point
-        // the system is bound to our (spooler) process which
-        // guarantees that this process will not be killed.
-        if (mController != null && mController.hasStarted()) {
-            mController.finish();
-        }
-        if (mEditor != null && mEditor.isPrintConfirmed()
-                && mController != null && mController.isFinished()) {
-                mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
-                        PrintJobInfo.STATE_QUEUED, null);
-        } else {
-            mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
-                    PrintJobInfo.STATE_CANCELED, null);
-        }
-        if (mGeneratingPrintJobDialog != null) {
-            mGeneratingPrintJobDialog.dismiss();
-            mGeneratingPrintJobDialog = null;
-        }
-        mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
-        mSpoolerProvider.destroy();
-        super.onDestroy();
+    public void onPause() {
+       if (isFinishing()) {
+           if (mController != null && mController.hasStarted()) {
+               mController.finish();
+           }
+           if (mEditor != null && mEditor.isPrintConfirmed()
+                   && mController != null && mController.isFinished()) {
+                   mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
+                           PrintJobInfo.STATE_QUEUED, null);
+           } else {
+               mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
+                       PrintJobInfo.STATE_CANCELED, null);
+           }
+           if (mGeneratingPrintJobDialog != null) {
+               mGeneratingPrintJobDialog.dismiss();
+               mGeneratingPrintJobDialog = null;
+           }
+           mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
+           mSpoolerProvider.destroy();
+       }
+        super.onPause();
     }
 
     public boolean onTouchEvent(MotionEvent event) {
@@ -607,18 +612,14 @@
             } else {
                 // We did not get the pages we requested, then the application
                 // misbehaves, so we fail quickly.
-                // TODO: We need some UI for announcing an error.
                 mControllerState = CONTROLLER_STATE_FAILED;
                 Log.e(LOG_TAG, "Received invalid pages from the app");
-                mEditor.cancel();
-                PrintJobConfigActivity.this.finish();
+                mEditor.showUi(Editor.UI_ERROR, null);
             }
         }
 
         private void requestCreatePdfFileOrFinish() {
             if (mEditor.isPrintingToPdf()) {
-                PrintJobInfo printJob = mSpoolerProvider.getSpooler()
-                        .getPrintJobInfo(mPrintJobId, PrintManager.APP_ID_ANY);
                 Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
                 intent.setType("application/pdf");
                 intent.putExtra(Intent.EXTRA_TITLE, mDocument.info.getName());
@@ -791,6 +792,19 @@
                 }
                 mEditor.ensureCurrentPrinterSelected();
             } break;
+
+            case ACTIVITY_POPULATE_ADVANCED_PRINT_OPTIONS: {
+                if (resultCode == RESULT_OK) {
+                    PrintJobInfo printJobInfo = (PrintJobInfo) data.getParcelableExtra(
+                            PrintService.EXTRA_PRINT_JOB_INFO);
+                    if (printJobInfo != null) {
+                        mEditor.updateFromAdvancedOptions(printJobInfo);
+                        break;
+                    }
+                }
+                mEditor.cancel();
+                PrintJobConfigActivity.this.finish();
+            } break;
         }
     }
 
@@ -869,6 +883,10 @@
 
         private View mContentContainer;
 
+        private View mAdvancedPrintOptionsContainer;
+
+        private Button mAdvancedOptionsButton;
+
         private Button mPrintButton;
 
         private PrinterId mNextPrinterId;
@@ -932,6 +950,10 @@
                         refreshCurrentPrinter();
                     }
                 } else if (spinner == mMediaSizeSpinner) {
+                    if (mIgnoreNextMediaSizeChange) {
+                        mIgnoreNextMediaSizeChange = false;
+                        return;
+                    }
                     if (mOldMediaSizeSelectionIndex
                             == mMediaSizeSpinner.getSelectedItemPosition()) {
                         mOldMediaSizeSelectionIndex = AdapterView.INVALID_POSITION;
@@ -947,6 +969,10 @@
                         mController.update();
                     }
                 } else if (spinner == mColorModeSpinner) {
+                    if (mIgnoreNextColorChange) {
+                        mIgnoreNextColorChange = false;
+                        return;
+                    }
                     if (mOldColorModeSelectionIndex
                             == mColorModeSpinner.getSelectedItemPosition()) {
                         mOldColorModeSelectionIndex = AdapterView.INVALID_POSITION;
@@ -1193,6 +1219,16 @@
                 // greater than the to page. When computing the requested pages
                 // we just swap them if necessary.
 
+                // Keep the print job up to date with the selected pages if we
+                // know how many pages are there in the document.
+                PageRange[] requestedPages = getRequestedPages();
+                if (requestedPages != null && requestedPages.length > 0
+                        && requestedPages[requestedPages.length - 1].getEnd()
+                                < mDocument.info.getPageCount()) {
+                    mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(
+                            mPrintJobId, requestedPages);
+                }
+
                 mPageRangeEditText.setError(null);
                 mPrintButton.setEnabled(true);
                 updateUi();
@@ -1215,6 +1251,8 @@
         private boolean mIgnoreNextRangeOptionChange;
         private boolean mIgnoreNextCopiesChange;
         private boolean mIgnoreNextRangeChange;
+        private boolean mIgnoreNextMediaSizeChange;
+        private boolean mIgnoreNextColorChange;
 
         private int mCurrentUi = UI_NONE;
 
@@ -1424,6 +1462,88 @@
             }
         }
 
+        public void updateFromAdvancedOptions(PrintJobInfo printJobInfo) {
+            boolean updateContent = false;
+
+            // Copies.
+            mCopiesEditText.setText(String.valueOf(printJobInfo.getCopies()));
+
+            // Media size and orientation
+            PrintAttributes attributes = printJobInfo.getAttributes();
+            if (!mCurrPrintAttributes.getMediaSize().equals(attributes.getMediaSize())) {
+                final int mediaSizeCount = mMediaSizeSpinnerAdapter.getCount();
+                for (int i = 0; i < mediaSizeCount; i++) {
+                    MediaSize mediaSize = mMediaSizeSpinnerAdapter.getItem(i).value;
+                    if (mediaSize.asPortrait().equals(attributes.getMediaSize().asPortrait())) {
+                        updateContent = true;
+                        mCurrPrintAttributes.setMediaSize(attributes.getMediaSize());
+                        mMediaSizeSpinner.setSelection(i);
+                        mIgnoreNextMediaSizeChange = true;
+                        if (attributes.getMediaSize().isPortrait()) {
+                            mOrientationSpinner.setSelection(0);
+                            mIgnoreNextOrientationChange = true;
+                        } else {
+                            mOrientationSpinner.setSelection(1);
+                            mIgnoreNextOrientationChange = true;
+                        }
+                        break;
+                    }
+                }
+            }
+
+            // Color mode.
+            final int colorMode = attributes.getColorMode();
+            if (mCurrPrintAttributes.getColorMode() != colorMode) {
+                if (colorMode == PrintAttributes.COLOR_MODE_MONOCHROME) {
+                    updateContent = true;
+                    mColorModeSpinner.setSelection(0);
+                    mIgnoreNextColorChange = true;
+                    mCurrPrintAttributes.setColorMode(attributes.getColorMode());
+                } else if (colorMode == PrintAttributes.COLOR_MODE_COLOR) {
+                    updateContent = true;
+                    mColorModeSpinner.setSelection(1);
+                    mIgnoreNextColorChange = true;
+                    mCurrPrintAttributes.setColorMode(attributes.getColorMode());
+                }
+            }
+
+            // Range.
+            PageRange[] pageRanges = printJobInfo.getPages();
+            if (pageRanges != null && pageRanges.length > 0) {
+                pageRanges = PageRangeUtils.normalize(pageRanges);
+                final int pageRangeCount = pageRanges.length;
+                if (pageRangeCount == 1 && pageRanges[0] == PageRange.ALL_PAGES) {
+                    mRangeOptionsSpinner.setSelection(0);
+                } else {
+                    final int pageCount = mDocument.info.getPageCount();
+                    if (pageRanges[0].getStart() >= 0
+                            && pageRanges[pageRanges.length - 1].getEnd() < pageCount) {
+                        mRangeOptionsSpinner.setSelection(1);
+                        StringBuilder builder = new StringBuilder();
+                        for (int i = 0; i < pageRangeCount; i++) {
+                            if (builder.length() > 0) {
+                                builder.append(',');
+                            }
+                            PageRange pageRange = pageRanges[i];
+                            builder.append(pageRange.getStart());
+                            builder.append('-');
+                            builder.append(pageRange.getEnd());
+                        }
+                        mPageRangeEditText.setText(builder.toString());
+                    }
+                }
+            }
+
+            // Update the advanced options.
+            mSpoolerProvider.getSpooler().setPrintJobAdvancedOptionsNoPersistence(
+                    mPrintJobId, printJobInfo.getAdvancedOptions());
+
+            // Update the content if needed.
+            if (updateContent) {
+                mController.update();
+            }
+        }
+
         public void ensurePrinterSelected(PrinterId printerId) {
             // If the printer is not present maybe the loader is not
             // updated yet. In this case make a note and as soon as
@@ -1609,6 +1729,44 @@
             }
         }
 
+        private void registerAdvancedPrintOptionsButtonClickListener() {
+            Button advancedOptionsButton = (Button) findViewById(R.id.advanced_settings_button);
+            advancedOptionsButton.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    ComponentName serviceName = mCurrentPrinter.getId().getServiceName();
+                    String activityName = getAdvancedOptionsActivityName(serviceName);
+                    if (TextUtils.isEmpty(activityName)) {
+                        return;
+                    }
+                    Intent intent = new Intent(Intent.ACTION_MAIN);
+                    intent.setComponent(new ComponentName(serviceName.getPackageName(),
+                            activityName));
+
+                    List<ResolveInfo> resolvedActivities = getPackageManager()
+                            .queryIntentActivities(intent, 0);
+                    if (resolvedActivities.isEmpty()) {
+                        return;
+                    }
+                    // The activity is a component name, therefore it is one or none.
+                    if (resolvedActivities.get(0).activityInfo.exported) {
+                        PrintJobInfo printJobInfo = mSpoolerProvider.getSpooler().getPrintJobInfo(
+                                mPrintJobId, PrintManager.APP_ID_ANY);
+                        intent.putExtra(PrintService.EXTRA_PRINT_JOB_INFO, printJobInfo);
+                        // TODO: Make this an API for the next release.
+                        intent.putExtra("android.intent.extra.print.EXTRA_PRINTER_INFO",
+                                mCurrentPrinter);
+                        try {
+                            startActivityForResult(intent,
+                                    ACTIVITY_POPULATE_ADVANCED_PRINT_OPTIONS);
+                        } catch (ActivityNotFoundException anfe) {
+                            Log.e(LOG_TAG, "Error starting activity for intent: " + intent, anfe);
+                        }
+                    }
+                }
+            });
+        }
+
         private void registerPrintButtonClickListener() {
             Button printButton = (Button) findViewById(R.id.print_button);
             printButton.setOnClickListener(new OnClickListener() {
@@ -1656,6 +1814,9 @@
                             mEditor.initialize();
                             mEditor.bindUi();
                             mEditor.reselectCurrentPrinter();
+                            if (!mController.hasPerformedLayout()) {
+                                mController.update();
+                            }
                         }
                     });
                 }
@@ -1857,6 +2018,11 @@
             mPageRangeEditText.setOnFocusChangeListener(mFocusListener);
             mPageRangeEditText.addTextChangedListener(mRangeTextWatcher);
 
+            // Advanced options button.
+            mAdvancedPrintOptionsContainer = findViewById(R.id.advanced_settings_container);
+            mAdvancedOptionsButton = (Button) findViewById(R.id.advanced_settings_button);
+            registerAdvancedPrintOptionsButtonClickListener();
+
             // Print button
             mPrintButton = (Button) findViewById(R.id.print_button);
             registerPrintButtonClickListener();
@@ -1875,6 +2041,7 @@
                 mRangeOptionsSpinner.setEnabled(false);
                 mPageRangeEditText.setEnabled(false);
                 mPrintButton.setEnabled(false);
+                mAdvancedOptionsButton.setEnabled(false);
                 return false;
             }
 
@@ -1900,6 +2067,7 @@
                 mRangeOptionsSpinner.setEnabled(false);
                 mPageRangeEditText.setEnabled(false);
                 mPrintButton.setEnabled(false);
+                mAdvancedOptionsButton.setEnabled(false);
                 return false;
             } else {
                 boolean someAttributeSelectionChanged = false;
@@ -2077,7 +2245,17 @@
                     mPageRangeTitle.setVisibility(View.INVISIBLE);
                 }
 
-                // Print/Print preview
+                // Advanced print options
+                ComponentName serviceName = mCurrentPrinter.getId().getServiceName();
+                if (!TextUtils.isEmpty(getAdvancedOptionsActivityName(serviceName))) {
+                    mAdvancedPrintOptionsContainer.setVisibility(View.VISIBLE);
+                    mAdvancedOptionsButton.setEnabled(true);
+                } else {
+                    mAdvancedPrintOptionsContainer.setVisibility(View.GONE);
+                    mAdvancedOptionsButton.setEnabled(false);
+                }
+
+                // Print
                 if (mDestinationSpinner.getSelectedItemId()
                         != DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF) {
                     String newText = getString(R.string.print_button);
@@ -2117,6 +2295,21 @@
             }
         }
 
+        private String getAdvancedOptionsActivityName(ComponentName serviceName) {
+            PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
+            List<PrintServiceInfo> printServices = printManager.getEnabledPrintServices();
+            final int printServiceCount = printServices.size();
+            for (int i = 0; i < printServiceCount; i ++) {
+                PrintServiceInfo printServiceInfo = printServices.get(i);
+                ServiceInfo serviceInfo = printServiceInfo.getResolveInfo().serviceInfo;
+                if (serviceInfo.name.equals(serviceName.getClassName())
+                        && serviceInfo.packageName.equals(serviceName.getPackageName())) {
+                    return printServiceInfo.getAdvancedOptionsActivityName();
+                }
+            }
+            return null;
+        }
+
         private void setMediaSizeSpinnerSelectionNoCallback(int position) {
             if (mMediaSizeSpinner.getSelectedItemPosition() != position) {
                 mOldMediaSizeSelectionIndex = position;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
index 636e245..609ae64 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.AsyncTask;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
@@ -623,6 +624,16 @@
         }
     }
 
+    public void setPrintJobAdvancedOptionsNoPersistence(PrintJobId printJobId,
+            Bundle advancedOptions) {
+        synchronized (mLock) {
+            PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
+            if (printJob != null) {
+                printJob.setAdvancedOptions(advancedOptions);
+            }
+        }
+    }
+
     public void setPrintJobPrintDocumentInfoNoPersistence(PrintJobId printJobId,
             PrintDocumentInfo info) {
         synchronized (mLock) {
@@ -704,6 +715,14 @@
         private static final String ATTR_STATE_REASON = "stateReason";
         private static final String ATTR_CANCELLING = "cancelling";
 
+        private static final String TAG_ADVANCED_OPTIONS = "advancedOptions";
+        private static final String TAG_ADVANCED_OPTION = "advancedOption";
+        private static final String ATTR_KEY = "key";
+        private static final String ATTR_TYPE = "type";
+        private static final String ATTR_VALUE = "value";
+        private static final String TYPE_STRING = "string";
+        private static final String TYPE_INT = "int";
+
         private static final String TAG_MEDIA_SIZE = "mediaSize";
         private static final String TAG_RESOLUTION = "resolution";
         private static final String TAG_MARGINS = "margins";
@@ -899,6 +918,30 @@
                         serializer.endTag(null, TAG_DOCUMENT_INFO);
                     }
 
+                    Bundle advancedOptions = printJob.getAdvancedOptions();
+                    if (advancedOptions != null) {
+                        serializer.startTag(null, TAG_ADVANCED_OPTIONS);
+                        for (String key : advancedOptions.keySet()) {
+                            Object value = advancedOptions.get(key);
+                            if (value instanceof String) {
+                                String stringValue = (String) value;
+                                serializer.startTag(null, TAG_ADVANCED_OPTION);
+                                serializer.attribute(null, ATTR_KEY, key);
+                                serializer.attribute(null, ATTR_TYPE, TYPE_STRING);
+                                serializer.attribute(null, ATTR_VALUE, stringValue);
+                                serializer.endTag(null, TAG_ADVANCED_OPTION);
+                            } else if (value instanceof Integer) {
+                                String intValue = Integer.toString((Integer) value);
+                                serializer.startTag(null, TAG_ADVANCED_OPTION);
+                                serializer.attribute(null, ATTR_KEY, key);
+                                serializer.attribute(null, ATTR_TYPE, TYPE_INT);
+                                serializer.attribute(null, ATTR_VALUE, intValue);
+                                serializer.endTag(null, TAG_ADVANCED_OPTION);
+                            }
+                        }
+                        serializer.endTag(null, TAG_ADVANCED_OPTIONS);
+                    }
+
                     serializer.endTag(null, TAG_JOB);
 
                     if (DEBUG_PERSISTENCE) {
@@ -1027,6 +1070,7 @@
                 skipEmptyTextTags(parser);
                 expect(parser, XmlPullParser.END_TAG, TAG_PAGE_RANGE);
                 parser.next();
+                skipEmptyTextTags(parser);
             }
             if (pageRanges != null) {
                 PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
@@ -1057,8 +1101,8 @@
                     final int labelResId = (labelResIdString != null)
                             ? Integer.parseInt(labelResIdString) : 0;
                     label = parser.getAttributeValue(null, ATTR_LABEL);
-                    MediaSize mediaSize = new MediaSize(id, label, packageName, labelResId,
-                                widthMils, heightMils);
+                    MediaSize mediaSize = new MediaSize(id, label, packageName,
+                                widthMils, heightMils, labelResId);
                     builder.setMediaSize(mediaSize);
                     parser.next();
                     skipEmptyTextTags(parser);
@@ -1127,6 +1171,32 @@
                 parser.next();
             }
 
+            skipEmptyTextTags(parser);
+            if (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTIONS)) {
+                parser.next();
+                skipEmptyTextTags(parser);
+                Bundle advancedOptions = new Bundle();
+                while (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTION)) {
+                    String key = parser.getAttributeValue(null, ATTR_KEY);
+                    String value = parser.getAttributeValue(null, ATTR_VALUE);
+                    String type = parser.getAttributeValue(null, ATTR_TYPE);
+                    if (TYPE_STRING.equals(type)) {
+                        advancedOptions.putString(key, value);
+                    } else if (TYPE_INT.equals(type)) {
+                        advancedOptions.putInt(key, Integer.valueOf(value));
+                    }
+                    parser.next();
+                    skipEmptyTextTags(parser);
+                    expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTION);
+                    parser.next();
+                    skipEmptyTextTags(parser);
+                }
+                printJob.setAdvancedOptions(advancedOptions);
+                skipEmptyTextTags(parser);
+                expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTIONS);
+                parser.next();
+            }
+
             mPrintJobs.add(printJob);
 
             if (DEBUG_PERSISTENCE) {
diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java
index b69dcee..f23a992 100644
--- a/services/java/com/android/server/print/UserState.java
+++ b/services/java/com/android/server/print/UserState.java
@@ -231,10 +231,10 @@
         for (int i = 0; i < cachedPrintJobCount; i++) {
             PrintJobInfo cachedPrintJob = cachedPrintJobs.get(i);
             result.put(cachedPrintJob.getId(), cachedPrintJob);
-            // Strip out the tag - it is visible only to print services.
-            // Also the cached print jobs are delivered only to apps, so
-            // stripping the tag of a cached print job is fine.
+            // Strip out the tag and the advanced print options.
+            // They are visible only to print services.
             cachedPrintJob.setTag(null);
+            cachedPrintJob.setAdvancedOptions(null);
         }
 
         // Add everything else the spooler knows about.
@@ -245,8 +245,10 @@
             for (int i = 0; i < printJobCount; i++) {
                 PrintJobInfo printJob = printJobs.get(i);
                 result.put(printJob.getId(), printJob);
-                // Strip out the tag - it is visible only to print services.
+                // Strip out the tag and the advanced print options.
+                // They are visible only to print services.
                 printJob.setTag(null);
+                printJob.setAdvancedOptions(null);
             }
         }
 
@@ -255,10 +257,16 @@
 
     public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
         PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId);
-        if (printJob != null) {
-            return printJob;
+        if (printJob == null) {
+            printJob = mSpooler.getPrintJobInfo(printJobId, appId);
         }
-        return mSpooler.getPrintJobInfo(printJobId, appId);
+        if (printJob != null) {
+            // Strip out the tag and the advanced print options.
+            // They are visible only to print services.
+            printJob.setTag(null);
+            printJob.setAdvancedOptions(null);
+        }
+        return printJob;
     }
 
     public void cancelPrintJob(PrintJobId printJobId, int appId) {