OSDN Git Service

Print UI polish (a.k.a. just the next iteration))
authorSvetoslav Ganov <svetoslavganov@google.com>
Tue, 6 Aug 2013 21:40:46 +0000 (14:40 -0700)
committerSvetoslav Ganov <svetoslavganov@google.com>
Tue, 6 Aug 2013 22:07:17 +0000 (15:07 -0700)
1. Added a dialog to show a spinner while the app is writing the
   printed content.

2. Fixed print job config acitivity leaking.

3. Updated the notifications a bit.

Change-Id: I8314390135a49605ee11ab4ed14b210a29566745

core/java/android/print/PrintManager.java
packages/PrintSpooler/res/layout/generating_print_job_dialog.xml [new file with mode: 0644]
packages/PrintSpooler/res/layout/print_job_config_activity.xml
packages/PrintSpooler/res/values/strings.xml
packages/PrintSpooler/src/com/android/printspooler/NotificationController.java
packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java

index 9e8cfad..c067661 100644 (file)
@@ -288,6 +288,7 @@ public final class PrintManager {
         private void doFinish() {
             mDocumentAdapter = null;
             mHandler = null;
+            mLayoutOrWriteCancellation = null;
         }
 
         private final class MyHandler extends Handler {
@@ -312,10 +313,10 @@ public final class PrintManager {
 
                     case MSG_LAYOUT: {
                         SomeArgs args = (SomeArgs) message.obj;
-                        final PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
-                        final PrintAttributes newAttributes = (PrintAttributes) args.arg2;
-                        final ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
-                        final Bundle metadata = (Bundle) args.arg4;
+                        PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
+                        PrintAttributes newAttributes = (PrintAttributes) args.arg2;
+                        ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
+                        Bundle metadata = (Bundle) args.arg4;
                         final int sequence = args.argi1;
                         args.recycle();
 
@@ -324,49 +325,15 @@ public final class PrintManager {
                             mLayoutOrWriteCancellation = cancellation;
                         }
 
-                        mDocumentAdapter.onLayout(oldAttributes, newAttributes,
-                                cancellation, new LayoutResultCallback() {
-                            @Override
-                            public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
-                                if (info == null) {
-                                    throw new IllegalArgumentException("info cannot be null");
-                                }
-                                synchronized (mLock) {
-                                    mLayoutOrWriteCancellation = null;
-                                }
-                                try {
-                                    callback.onLayoutFinished(info, changed, sequence);
-                                } catch (RemoteException re) {
-                                    Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
-                                }
-                            }
-
-                            @Override
-                            public void onLayoutFailed(CharSequence error) {
-                                synchronized (mLock) {
-                                    mLayoutOrWriteCancellation = null;
-                                }
-                                try {
-                                    callback.onLayoutFailed(error, sequence);
-                                } catch (RemoteException re) {
-                                    Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
-                                }
-                            }
-
-                            @Override
-                            public void onLayoutCancelled() {
-                                synchronized (mLock) {
-                                    mLayoutOrWriteCancellation = null;
-                                }
-                            }
-                        }, metadata);
+                        mDocumentAdapter.onLayout(oldAttributes, newAttributes, cancellation,
+                                new MyLayoutResultCallback(callback, sequence), metadata);
                     } break;
 
                     case MSG_WRITE: {
                         SomeArgs args = (SomeArgs) message.obj;
-                        final PageRange[] pages = (PageRange[]) args.arg1;
-                        final FileDescriptor fd = (FileDescriptor) args.arg2;
-                        final IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
+                        PageRange[] pages = (PageRange[]) args.arg1;
+                        FileDescriptor fd = (FileDescriptor) args.arg2;
+                        IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
                         final int sequence = args.argi1;
                         args.recycle();
 
@@ -376,52 +343,7 @@ public final class PrintManager {
                         }
 
                         mDocumentAdapter.onWrite(pages, fd, cancellation,
-                                new WriteResultCallback() {
-                            @Override
-                            public void onWriteFinished(PageRange[] pages) {
-                                if (pages == null) {
-                                    throw new IllegalArgumentException("pages cannot be null");
-                                }
-                                if (pages.length == 0) {
-                                    throw new IllegalArgumentException("pages cannot be empty");
-                                }
-                                synchronized (mLock) {
-                                    mLayoutOrWriteCancellation = null;
-                                }
-                                // Close before notifying the other end. We want
-                                // to be ready by the time we announce it.
-                                IoUtils.closeQuietly(fd);
-                                try {
-                                    callback.onWriteFinished(pages, sequence);
-                                } catch (RemoteException re) {
-                                    Log.e(LOG_TAG, "Error calling onWriteFinished", re);
-                                }
-                            }
-
-                            @Override
-                            public void onWriteFailed(CharSequence error) {
-                                synchronized (mLock) {
-                                    mLayoutOrWriteCancellation = null;
-                                }
-                                // Close before notifying the other end. We want
-                                // to be ready by the time we announce it.
-                                IoUtils.closeQuietly(fd);
-                                try {
-                                    callback.onWriteFailed(error, sequence);
-                                } catch (RemoteException re) {
-                                    Log.e(LOG_TAG, "Error calling onWriteFailed", re);
-                                }
-                            }
-
-                            @Override
-                            public void onWriteCancelled() {
-                                synchronized (mLock) {
-                                    mLayoutOrWriteCancellation = null;
-                                }
-                                // Just close the fd for now.
-                                IoUtils.closeQuietly(fd);
-                            }
-                        });
+                                new MyWriteResultCallback(callback, fd, sequence));
                     } break;
 
                     case MSG_FINISH: {
@@ -436,5 +358,128 @@ public final class PrintManager {
                 }
             }
         }
+
+        private final class MyLayoutResultCallback extends LayoutResultCallback {
+            private ILayoutResultCallback mCallback;
+            private final int mSequence;
+
+            public MyLayoutResultCallback(ILayoutResultCallback callback,
+                    int sequence) {
+                mCallback = callback;
+                mSequence = sequence;
+            }
+
+            @Override
+            public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+                final ILayoutResultCallback callback;
+                synchronized (mLock) {
+                    callback = mCallback;
+                    clearLocked();
+                }
+                if (info == null) {
+                    throw new IllegalArgumentException("info cannot be null");
+                }
+                if (callback != null) {
+                    try {
+                        callback.onLayoutFinished(info, changed, mSequence);
+                    } catch (RemoteException re) {
+                        Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
+                    }
+                }
+            }
+
+            @Override
+            public void onLayoutFailed(CharSequence error) {
+                final ILayoutResultCallback callback;
+                synchronized (mLock) {
+                    callback = mCallback;
+                    clearLocked();
+                }
+                if (callback != null) {
+                    try {
+                        callback.onLayoutFailed(error, mSequence);
+                    } catch (RemoteException re) {
+                        Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
+                    }
+                }
+            }
+
+            @Override
+            public void onLayoutCancelled() {
+                synchronized (mLock) {
+                    clearLocked();
+                }
+            }
+
+            private void clearLocked() {
+                mLayoutOrWriteCancellation = null;
+                mCallback = null;
+            }
+        }
+
+        private final class MyWriteResultCallback extends WriteResultCallback {
+            private FileDescriptor mFd;
+            private int mSequence;
+            private IWriteResultCallback mCallback;
+
+            public MyWriteResultCallback(IWriteResultCallback callback,
+                    FileDescriptor fd, int sequence) {
+                mFd = fd;
+                mSequence = sequence;
+                mCallback = callback;
+            }
+
+            @Override
+            public void onWriteFinished(PageRange[] pages) {
+                final IWriteResultCallback callback;
+                synchronized (mLock) {
+                    callback = mCallback;
+                    clearLocked();
+                }
+                if (pages == null) {
+                    throw new IllegalArgumentException("pages cannot be null");
+                }
+                if (pages.length == 0) {
+                    throw new IllegalArgumentException("pages cannot be empty");
+                }
+                if (callback != null) {
+                    try {
+                        callback.onWriteFinished(pages, mSequence);
+                    } catch (RemoteException re) {
+                        Log.e(LOG_TAG, "Error calling onWriteFinished", re);
+                    }
+                }
+            }
+
+            @Override
+            public void onWriteFailed(CharSequence error) {
+                final IWriteResultCallback callback;
+                synchronized (mLock) {
+                    callback = mCallback;
+                    clearLocked();
+                }
+                if (callback != null) {
+                    try {
+                        callback.onWriteFailed(error, mSequence);
+                    } catch (RemoteException re) {
+                        Log.e(LOG_TAG, "Error calling onWriteFailed", re);
+                    }
+                }
+            }
+
+            @Override
+            public void onWriteCancelled() {
+                synchronized (mLock) {
+                    clearLocked();
+                }
+            }
+
+            private void clearLocked() {
+                mLayoutOrWriteCancellation = null;
+                IoUtils.closeQuietly(mFd);
+                mCallback = null;
+                mFd = null;
+            }
+        }
     }
 }
diff --git a/packages/PrintSpooler/res/layout/generating_print_job_dialog.xml b/packages/PrintSpooler/res/layout/generating_print_job_dialog.xml
new file mode 100644 (file)
index 0000000..360f843
--- /dev/null
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/progress"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:layout_margin="16dip"
+    android:layout_gravity="center_horizontal"
+    style="?android:attr/progressBarStyleLarge">
+</ProgressBar>
index a4105ea..1a8b0f1 100644 (file)
      limitations under the License.
 -->
 
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:scrollbars="vertical">
+    android:orientation="vertical">
 
-    <GridLayout
-        android:layout_width="wrap_content"
+    <ScrollView
+        android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical"
-        android:columnCount="2">
+        android:scrollbars="vertical">
 
-        <!-- Destination -->
-
-        <Spinner
-            android:id="@+id/destination_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="fill_horizontal"
-            android:layout_marginLeft="32dip"
-            android:layout_marginTop="32dip"
-            android:layout_marginRight="32dip"
-            android:layout_marginBottom="12dip"
-            android:layout_row="0"
-            android:layout_column="0"
-            android:layout_columnSpan="2"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Spinner>
-
-        <!-- Copies -->
-
-        <view
-            class="com.android.printspooler.PrintJobConfigActivity$CustomEditText"
-            android:id="@+id/copies_edittext"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="32dip"
-            android:layout_marginRight="12dip"
-            android:layout_marginBottom="12dip"
-            android:layout_row="2"
-            android:layout_column="0"
-            android:layout_gravity="bottom"
-            android:inputType="numberDecimal"
-            android:selectAllOnFocus="true"
-            android:minWidth="150dip"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </view>
-
-        <TextView
+        <GridLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginLeft="32dip"
-            android:layout_marginTop="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="1"
-            android:layout_column="0"
-            android:layout_gravity="left|bottom"
-            android:text="@string/label_copies"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textStyle="bold"
-            android:labelFor="@id/copies_edittext">
-        </TextView>
-
-        <!-- Paper size -->
-
-        <Spinner
-            android:id="@+id/paper_size_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="32dip"
-            android:layout_marginBottom="12dip"
-            android:layout_row="2"
-            android:layout_column="1"
-            android:minWidth="150dip"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Spinner>
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="32dip"
-            android:layout_marginTop="12dip"
-            android:layout_row="1"
-            android:layout_column="1"
-            android:text="@string/label_paper_size"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textStyle="bold"
-            android:labelFor="@id/paper_size_spinner">
-        </TextView>
-
-        <!-- Color -->
-
-        <Spinner
-            android:id="@+id/color_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="32dip"
-            android:layout_marginRight="12dip"
-            android:layout_marginBottom="12dip"
-            android:layout_row="4"
-            android:layout_column="0"
-            android:minWidth="150dip"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Spinner>
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="32dip"
-            android:layout_marginTop="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="3"
-            android:layout_column="0"
-            android:text="@string/label_color"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textStyle="bold"
-            android:labelFor="@id/color_spinner">
-        </TextView>
-
-        <!-- Orientation -->
-
-        <Spinner
-            android:id="@+id/orientation_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="32dip"
-            android:layout_marginBottom="12dip"
-            android:layout_row="4"
-            android:layout_column="1"
-            android:minWidth="150dip"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Spinner>
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginTop="12dip"
-            android:layout_marginRight="32dip"
-            android:layout_row="3"
-            android:layout_column="1"
-            android:text="@string/label_orientation"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textStyle="bold"
-            android:labelFor="@id/orientation_spinner">
-        </TextView>
-
-        <!-- Pages -->
-
-        <Spinner
-            android:id="@+id/range_options_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="32dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="6"
-            android:layout_column="0"
-            android:minWidth="150dip"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Spinner>
-
-        <view
-            class="com.android.printspooler.PrintJobConfigActivity$CustomEditText"
-            android:id="@+id/page_range_edittext"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="32dip"
-            android:layout_row="6"
-            android:layout_column="1"
-            android:layout_gravity="bottom"
-            android:selectAllOnFocus="true"
-            android:minWidth="150dip"
-            android:hint="@string/pages_range_example"
-            android:inputType="textNoSuggestions"
-            android:visibility="gone"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </view>
-
-        <TextView
-            android:id="@+id/page_range_title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="32dip"
-            android:layout_marginTop="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="5"
-            android:layout_column="0"
-            android:text="@string/label_pages"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textStyle="bold"
-            android:labelFor="@id/range_options_spinner">
-        </TextView>
-
-        <!-- Print pereview  -->
-
-        <ImageView
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="fill_horizontal"
-            android:layout_marginLeft="32dip"
-            android:layout_marginTop="32dip"
-            android:layout_marginRight="32dip"
-            android:layout_row="7"
-            android:layout_column="0"
-            android:layout_columnSpan="2"
-            android:background="?android:attr/listDivider"
-            android:contentDescription="@null">
-        </ImageView>
-
-        <Button
-            android:id="@+id/print_preview_button"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="fill_horizontal"
-            android:layout_marginLeft="32dip"
-            android:layout_marginRight="32dip"
-            android:layout_row="8"
-            android:layout_column="0"
-            android:layout_columnSpan="2"
-            android:text="@string/print_preview"
-            android:gravity="left|center_vertical"
-            android:background="?android:attr/selectableItemBackground"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Button>
-
-        <ImageView
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="fill_horizontal"
-            android:layout_marginLeft="32dip"
-            android:layout_marginRight="32dip"
-            android:layout_marginBottom="32dip"
-            android:layout_row="9"
-            android:layout_column="0"
-            android:layout_columnSpan="2"
-            android:background="?android:attr/listDivider"
-            android:contentDescription="@null">
-        </ImageView>
-
-        <ImageView
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="fill_horizontal"
-            android:layout_row="10"
-            android:layout_column="0"
-            android:layout_columnSpan="2"
-            android:background="?android:attr/listDivider"
-            android:contentDescription="@null">
-        </ImageView>
-
-        <Button
-            android:id="@+id/print_button"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="fill_horizontal"
-            android:layout_row="11"
-            android:layout_column="0"
-            android:layout_columnSpan="2"
-            android:padding="0dip"
-            android:text="@string/print_button"
-            android:background="?android:attr/selectableItemBackground"
-            android:minHeight="?android:attr/listPreferredItemHeight">
-        </Button>
-
-    </GridLayout>
-
-</ScrollView>
+            android:orientation="vertical"
+            android:columnCount="2">
+
+            <!-- Destination -->
+
+            <Spinner
+                android:id="@+id/destination_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="fill_horizontal"
+                android:layout_marginLeft="32dip"
+                android:layout_marginTop="32dip"
+                android:layout_marginRight="32dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="0"
+                android:layout_column="0"
+                android:layout_columnSpan="2"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </Spinner>
+
+            <!-- Copies -->
+
+            <view
+                class="com.android.printspooler.PrintJobConfigActivity$CustomEditText"
+                android:id="@+id/copies_edittext"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="32dip"
+                android:layout_marginRight="12dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="2"
+                android:layout_column="0"
+                android:layout_gravity="bottom"
+                android:inputType="numberDecimal"
+                android:selectAllOnFocus="true"
+                android:minWidth="150dip"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </view>
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="32dip"
+                android:layout_marginTop="12dip"
+                android:layout_marginRight="12dip"
+                android:layout_row="1"
+                android:layout_column="0"
+                android:layout_gravity="left|bottom"
+                android:text="@string/label_copies"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/copies_edittext">
+            </TextView>
+
+            <!-- Paper size -->
+
+            <Spinner
+                android:id="@+id/paper_size_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginRight="32dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="2"
+                android:layout_column="1"
+                android:minWidth="150dip"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </Spinner>
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginRight="32dip"
+                android:layout_marginTop="12dip"
+                android:layout_row="1"
+                android:layout_column="1"
+                android:text="@string/label_paper_size"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/paper_size_spinner">
+            </TextView>
+
+            <!-- Color -->
+
+            <Spinner
+                android:id="@+id/color_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="32dip"
+                android:layout_marginRight="12dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="4"
+                android:layout_column="0"
+                android:minWidth="150dip"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </Spinner>
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="32dip"
+                android:layout_marginTop="12dip"
+                android:layout_marginRight="12dip"
+                android:layout_row="3"
+                android:layout_column="0"
+                android:text="@string/label_color"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/color_spinner">
+            </TextView>
+
+            <!-- Orientation -->
+
+            <Spinner
+                android:id="@+id/orientation_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginRight="32dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="4"
+                android:layout_column="1"
+                android:minWidth="150dip"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </Spinner>
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginTop="12dip"
+                android:layout_marginRight="32dip"
+                android:layout_row="3"
+                android:layout_column="1"
+                android:text="@string/label_orientation"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/orientation_spinner">
+            </TextView>
+
+            <!-- Pages -->
+
+            <Spinner
+                android:id="@+id/range_options_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="32dip"
+                android:layout_marginRight="12dip"
+                android:layout_row="6"
+                android:layout_column="0"
+                android:minWidth="150dip"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </Spinner>
+
+            <view
+                class="com.android.printspooler.PrintJobConfigActivity$CustomEditText"
+                android:id="@+id/page_range_edittext"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginRight="32dip"
+                android:layout_row="6"
+                android:layout_column="1"
+                android:layout_gravity="bottom"
+                android:selectAllOnFocus="true"
+                android:minWidth="150dip"
+                android:hint="@string/pages_range_example"
+                android:inputType="textNoSuggestions"
+                android:visibility="gone"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </view>
+
+            <TextView
+                android:id="@+id/page_range_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="32dip"
+                android:layout_marginTop="12dip"
+                android:layout_marginRight="12dip"
+                android:layout_row="5"
+                android:layout_column="0"
+                android:text="@string/label_pages"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/range_options_spinner">
+            </TextView>
+
+            <!-- Print pereview  -->
+
+            <ImageView
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="fill_horizontal"
+                android:layout_marginLeft="32dip"
+                android:layout_marginTop="32dip"
+                android:layout_marginRight="32dip"
+                android:layout_row="7"
+                android:layout_column="0"
+                android:layout_columnSpan="2"
+                android:background="?android:attr/listDivider"
+                android:contentDescription="@null">
+            </ImageView>
+
+            <Button
+                android:id="@+id/print_preview_button"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="fill_horizontal"
+                android:layout_marginLeft="32dip"
+                android:layout_marginRight="32dip"
+                android:layout_row="8"
+                android:layout_column="0"
+                android:layout_columnSpan="2"
+                android:text="@string/print_preview"
+                android:gravity="left|center_vertical"
+                android:background="?android:attr/selectableItemBackground"
+                android:minHeight="?android:attr/listPreferredItemHeight">
+            </Button>
+
+            <ImageView
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="fill_horizontal"
+                android:layout_marginLeft="32dip"
+                android:layout_marginRight="32dip"
+                android:layout_marginBottom="32dip"
+                android:layout_row="9"
+                android:layout_column="0"
+                android:layout_columnSpan="2"
+                android:background="?android:attr/listDivider"
+                android:contentDescription="@null">
+            </ImageView>
+
+            <ImageView
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="fill_horizontal"
+                android:layout_row="10"
+                android:layout_column="0"
+                android:layout_columnSpan="2"
+                android:background="?android:attr/listDivider"
+                android:contentDescription="@null">
+            </ImageView>
+
+        </GridLayout>
+
+    </ScrollView>
+
+    <Button
+        android:id="@+id/print_button"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="fill_horizontal"
+        android:text="@string/print_button"
+        style="?android:attr/buttonBarButtonStyle">
+    </Button>
+
+</LinearLayout>
index f400f21..fbddf43 100644 (file)
     <!-- Title if the number of pages in a printed document is unknown. [CHAR LIMIT=20] -->
     <string name="page_count_unknown">unknown</string>
 
-    <!-- Notifications -->
+    <!-- Title for the temporary dialog show while an app is generating a print job. [CHAR LIMIT=30] -->
+    <string name="generating_print_job">Generating print job</string>
 
-    <!-- Template for the notificaiton label for a queued print job. [CHAR LIMIT=25] -->
-    <string name="queued_notification_title_template">Queued <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
+    <!-- Notifications -->
 
     <!-- Template for the notificaiton label for a printing print job. [CHAR LIMIT=25] -->
     <string name="printing_notification_title_template">Printing <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
@@ -75,6 +75,9 @@
     <!-- Label for the notification button for restrating a filed print job. [CHAR LIMIT=25] -->
     <string name="restart">Restart</string>
 
+    <!-- Message that there is no connection to a printer. [CHAR LIMIT=40] -->
+    <string name="no_connection_to_printer">No connection to printer</string>
+
     <!-- Arrays -->
 
     <!-- Color mode labels. -->
index 14a96c9..8b49c0e 100644 (file)
@@ -65,10 +65,6 @@ public class NotificationController {
         }
         switch (printJob.getState()) {
             case PrintJobInfo.STATE_QUEUED: {
-                createQueuingNotificaiton(printJob);
-            } break;
-
-            case PrintJobInfo.STATE_STARTED: {
                 createPrintingNotificaiton(printJob);
             } break;
 
@@ -83,22 +79,6 @@ public class NotificationController {
         }
     }
 
-    private void createQueuingNotificaiton(PrintJobInfo printJob) {
-        Notification.Builder builder = new Notification.Builder(mContext)
-                // TODO: Use appropriate icon when assets are ready
-                .setSmallIcon(android.R.drawable.ic_secure)
-                .setContentTitle(mContext.getString(R.string.queued_notification_title_template,
-                        printJob.getLabel()))
-                // TODO: Use appropriate icon when assets are ready
-                .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel),
-                        createCancelIntent(printJob))
-                .setContentText(printJob.getPrinterId().getPrinterName())
-                .setWhen(System.currentTimeMillis())
-                .setOngoing(true)
-                .setShowWhen(true);
-        mNotificationManager.notify(printJob.getId(), builder.build());
-    }
-
     private void createPrintingNotificaiton(PrintJobInfo printJob) {
         Notification.Builder builder = new Notification.Builder(mContext)
                 // TODO: Use appropriate icon when assets are ready
index 15c2b2f..00a76b8 100644 (file)
 package com.android.printspooler;
 
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -66,6 +70,7 @@ import android.widget.Spinner;
 import android.widget.TextView;
 import android.widget.Toast;
 
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
@@ -137,6 +142,8 @@ public class PrintJobConfigActivity extends Activity {
 
     private IBinder mIPrintDocumentAdapter;
 
+    private Dialog mGeneratingPrintJobDialog;
+
     @Override
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
@@ -167,17 +174,14 @@ public class PrintJobConfigActivity extends Activity {
         mController = new PrintController(new RemotePrintDocumentAdapter(
                 IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter),
                 mSpooler.generateFileForPrintJob(mPrintJobId)));
-    }
 
-    @Override
-    protected void onResume() {
-        super.onResume();
         try {
             mIPrintDocumentAdapter.linkToDeath(mDeathRecipient, 0);
         } catch (RemoteException re) {
             finish();
             return;
         }
+
         mController.initialize();
         mEditor.initialize();
         mPrinterDiscoveryObserver = new PrinterDiscoveryObserver(mEditor, getMainLooper());
@@ -185,27 +189,29 @@ public class PrintJobConfigActivity extends Activity {
     }
 
     @Override
-    protected void onPause() {
+    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.
         mSpooler.stopPrinterDiscovery();
         mPrinterDiscoveryObserver.destroy();
         mPrinterDiscoveryObserver = null;
-        if (mController.isCancelled() || mController.isFailed()) {
+        if (mController.hasStarted()) {
+            mController.finish();
+        }
+        if (mEditor.isPrintConfirmed() && mController.isFinished()) {
+            mSpooler.setPrintJobState(mPrintJobId,
+                    PrintJobInfo.STATE_QUEUED, null);
+        } else {
             mSpooler.setPrintJobState(mPrintJobId,
                     PrintJobInfo.STATE_CANCELED, null);
-        } else if (mController.hasStarted()) {
-            mController.finish();
-            if (mEditor.isPrintConfirmed()) {
-                if (mController.isFinished()) {
-                    mSpooler.setPrintJobState(mPrintJobId,
-                            PrintJobInfo.STATE_QUEUED, null);
-                } else {
-                    mSpooler.setPrintJobState(mPrintJobId,
-                            PrintJobInfo.STATE_CANCELED, null);
-                }
-            }
         }
         mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
-        super.onPause();
+        if (mGeneratingPrintJobDialog != null) {
+            mGeneratingPrintJobDialog.dismiss();
+            mGeneratingPrintJobDialog = null;
+        }
+        super.onDestroy();
     }
 
     public boolean onTouchEvent(MotionEvent event) {
@@ -243,56 +249,54 @@ public class PrintJobConfigActivity extends Activity {
         return !mOldPrintAttributes.equals(mCurrPrintAttributes);
     }
 
+    private void showGeneratingPrintJobUi() {
+        getWindow().getDecorView().setVisibility(View.GONE);
+
+        DialogFragment fragment = new DialogFragment() {
+            @Override
+            public Dialog onCreateDialog(Bundle savedInstanceState) {
+                return new AlertDialog.Builder(PrintJobConfigActivity.this)
+                    .setTitle(getString(R.string.generating_print_job))
+                    .setView(PrintJobConfigActivity.this.getLayoutInflater().inflate(
+                            R.layout.generating_print_job_dialog, null))
+                    .setCancelable(false)
+                    .setPositiveButton(getString(R.string.cancel),
+                            new DialogInterface.OnClickListener() {
+                                @Override
+                                public void onClick(DialogInterface dialog, int which) {
+                                    mEditor.cancel();
+                                    PrintJobConfigActivity.this.finish();
+                                }
+                            })
+                    .create();
+            }
+        };
+        fragment.show(getFragmentManager(), getString(R.string.generating_print_job));
+    }
+
     private class PrintController {
         private final AtomicInteger mRequestCounter = new AtomicInteger();
 
         private final RemotePrintDocumentAdapter mRemotePrintAdapter;
 
-        private final Handler mHandler;
+        private final Bundle mMetadata;
 
-        private int mControllerState = CONTROLLER_STATE_INITIALIZED;
+        private final ControllerHandler mHandler;
 
-        private PageRange[] mRequestedPages;
-
-        private Bundle mMetadata = new Bundle();
+        private final LayoutResultCallback mLayoutResultCallback;
 
-        private final ILayoutResultCallback mILayoutResultCallback =
-                new ILayoutResultCallback.Stub() {
-            @Override
-            public void onLayoutFinished(PrintDocumentInfo info, boolean changed, int sequence) {
-                if (mRequestCounter.get() == sequence) {
-                    mHandler.obtainMessage(MyHandler.MSG_ON_LAYOUT_FINISHED, changed ? 1 : 0,
-                            0, info).sendToTarget();
-                }
-            }
-
-            @Override
-            public void onLayoutFailed(CharSequence error, int sequence) {
-                if (mRequestCounter.get() == sequence) {
-                    mHandler.obtainMessage(MyHandler.MSG_ON_LAYOUT_FAILED, error).sendToTarget();
-                }
-            }
-        };
+        private final WriteResultCallback mWriteResultCallback;
 
-        private IWriteResultCallback mIWriteResultCallback = new IWriteResultCallback.Stub() {
-            @Override
-            public void onWriteFinished(PageRange[] pages, int sequence) {
-                if (mRequestCounter.get() == sequence) {
-                    mHandler.obtainMessage(MyHandler.MSG_ON_WRITE_FINISHED, pages).sendToTarget();
-                }
-            }
+        private int mControllerState = CONTROLLER_STATE_INITIALIZED;
 
-            @Override
-            public void onWriteFailed(CharSequence error, int sequence) {
-                if (mRequestCounter.get() == sequence) {
-                    mHandler.obtainMessage(MyHandler.MSG_ON_WRITE_FAILED, error).sendToTarget();
-                }
-            }
-        };
+        private PageRange[] mRequestedPages;
 
         public PrintController(RemotePrintDocumentAdapter adapter) {
             mRemotePrintAdapter = adapter;
-            mHandler = new MyHandler(Looper.getMainLooper());
+            mMetadata = new Bundle();
+            mHandler = new ControllerHandler(getMainLooper());
+            mLayoutResultCallback = new LayoutResultCallback(mHandler);
+            mWriteResultCallback = new WriteResultCallback(mHandler);
         }
 
         public void initialize() {
@@ -311,10 +315,6 @@ public class PrintJobConfigActivity extends Activity {
             return (mControllerState == CONTROLLER_STATE_FINISHED);
         }
 
-        public boolean isFailed() {
-            return (mControllerState == CONTROLLER_STATE_FAILED);
-        }
-
         public boolean hasStarted() {
             return mControllerState >= CONTROLLER_STATE_STARTED;
         }
@@ -338,7 +338,7 @@ public class PrintJobConfigActivity extends Activity {
                 // If the attributes changes, then we do not do a layout but may
                 // have to ask the app to write some pages. Hence, pretend layout
                 // completed and nothing changed, so we handle writing as usual.
-                handleOnLayoutFinished(mDocument.info, false);
+                handleOnLayoutFinished(mDocument.info, false, mRequestCounter.get());
             } else {
                 mSpooler.setPrintJobAttributesNoPersistence(mPrintJobId, mCurrPrintAttributes);
 
@@ -348,7 +348,7 @@ public class PrintJobConfigActivity extends Activity {
                 mControllerState = CONTROLLER_STATE_LAYOUT_STARTED;
 
                 mRemotePrintAdapter.layout(mOldPrintAttributes, mCurrPrintAttributes,
-                        mILayoutResultCallback, mMetadata, mRequestCounter.incrementAndGet());
+                        mLayoutResultCallback, mMetadata, mRequestCounter.incrementAndGet());
 
                 mOldPrintAttributes.copyFrom(mCurrPrintAttributes);
             }
@@ -359,7 +359,12 @@ public class PrintJobConfigActivity extends Activity {
             mRemotePrintAdapter.finish();
         }
 
-        private void handleOnLayoutFinished(PrintDocumentInfo info, boolean layoutChanged) {
+        private void handleOnLayoutFinished(PrintDocumentInfo info,
+                boolean layoutChanged, int sequence) {
+            if (mRequestCounter.get() != sequence) {
+                return;
+            }
+
             if (isCancelled()) {
                 if (mEditor.isDone()) {
                     PrintJobConfigActivity.this.finish();
@@ -421,18 +426,25 @@ public class PrintJobConfigActivity extends Activity {
 
             // Request a write of the pages of interest.
             mControllerState = CONTROLLER_STATE_WRITE_STARTED;
-            mRemotePrintAdapter.write(mRequestedPages, mIWriteResultCallback,
+            mRemotePrintAdapter.write(mRequestedPages, mWriteResultCallback,
                     mRequestCounter.incrementAndGet());
         }
 
-        private void handleOnLayoutFailed(CharSequence error) {
+        private void handleOnLayoutFailed(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();
         }
 
-        private void handleOnWriteFinished(PageRange[] pages) {
+        private void handleOnWriteFinished(PageRange[] pages, int sequence) {
+            if (mRequestCounter.get() != sequence) {
+                return;
+            }
+
             if (isCancelled()) {
                 if (mEditor.isDone()) {
                     PrintJobConfigActivity.this.finish();
@@ -490,19 +502,22 @@ public class PrintJobConfigActivity extends Activity {
             }
         }
 
-        private void handleOnWriteFailed(CharSequence error) {
+        private void handleOnWriteFailed(CharSequence error, int sequence) {
+            if (mRequestCounter.get() != sequence) {
+                return;
+            }
             mControllerState = CONTROLLER_STATE_FAILED;
             Log.e(LOG_TAG, "Error during write: " + error);
             PrintJobConfigActivity.this.finish();
         }
 
-        private final class MyHandler extends Handler {
+        private final class ControllerHandler extends Handler {
             public static final int MSG_ON_LAYOUT_FINISHED = 1;
             public static final int MSG_ON_LAYOUT_FAILED = 2;
             public static final int MSG_ON_WRITE_FINISHED = 3;
             public static final int MSG_ON_WRITE_FAILED = 4;
 
-            public MyHandler(Looper looper) {
+            public ControllerHandler(Looper looper) {
                 super(looper, null, false);
             }
 
@@ -512,28 +527,84 @@ public class PrintJobConfigActivity extends Activity {
                     case MSG_ON_LAYOUT_FINISHED: {
                         PrintDocumentInfo info = (PrintDocumentInfo) message.obj;
                         final boolean changed = (message.arg1 == 1);
-                        mController.handleOnLayoutFinished(info, changed);
+                        final int sequence = message.arg2;
+                        handleOnLayoutFinished(info, changed, sequence);
                     } break;
 
                     case MSG_ON_LAYOUT_FAILED: {
                         CharSequence error = (CharSequence) message.obj;
-                        mController.handleOnLayoutFailed(error);
+                        final int sequence = message.arg1;
+                        handleOnLayoutFailed(error, sequence);
                     } break;
 
                     case MSG_ON_WRITE_FINISHED: {
                         PageRange[] pages = (PageRange[]) message.obj;
-                        mController.handleOnWriteFinished(pages);
+                        final int sequence = message.arg1;
+                        handleOnWriteFinished(pages, sequence);
                     } break;
 
                     case MSG_ON_WRITE_FAILED: {
                         CharSequence error = (CharSequence) message.obj;
-                        mController.handleOnWriteFailed(error);
+                        final int sequence = message.arg1;
+                        handleOnWriteFailed(error, sequence);
                     } break;
                 }
             }
         }
     }
 
+    private static final class LayoutResultCallback extends ILayoutResultCallback.Stub {
+        private final WeakReference<PrintController.ControllerHandler> mWeakHandler;
+
+        public LayoutResultCallback(PrintController.ControllerHandler handler) {
+            mWeakHandler = new WeakReference<PrintController.ControllerHandler>(handler);
+        }
+
+        @Override
+        public void onLayoutFinished(PrintDocumentInfo info, boolean changed, int sequence) {
+            Handler handler = mWeakHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_LAYOUT_FINISHED,
+                        changed ? 1 : 0, sequence, info).sendToTarget();
+            }
+        }
+
+        @Override
+        public void onLayoutFailed(CharSequence error, int sequence) {
+            Handler handler = mWeakHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_LAYOUT_FAILED,
+                        sequence, 0, error).sendToTarget();
+            }
+        }
+    }
+
+    private static final class WriteResultCallback extends IWriteResultCallback.Stub {
+        private final WeakReference<PrintController.ControllerHandler> mWeakHandler;
+
+        public WriteResultCallback(PrintController.ControllerHandler handler) {
+            mWeakHandler = new WeakReference<PrintController.ControllerHandler>(handler);
+        }
+
+        @Override
+        public void onWriteFinished(PageRange[] pages, int sequence) {
+            Handler handler = mWeakHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_WRITE_FINISHED,
+                        sequence, 0, pages).sendToTarget();
+            }
+        }
+
+        @Override
+        public void onWriteFailed(CharSequence error, int sequence) {
+            Handler handler = mWeakHandler.get();
+            if (handler != null) {
+                handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_WRITE_FAILED,
+                    sequence, 0, error).sendToTarget();
+            }
+        }
+    }
+
     private final class Editor {
         private final EditText mCopiesEditText;
 
@@ -837,6 +908,7 @@ public class PrintJobConfigActivity extends Activity {
                     mEditor.confirmPrint();
                     updateUi();
                     mController.update();
+                    showGeneratingPrintJobUi();
                 }
             });
 
index 0bc20a3..00e05bb 100644 (file)
@@ -177,17 +177,15 @@ public class PrintSpooler {
             // Update the notification.
             mNotificationController.onPrintJobStateChanged(printJob);
 
-            //TODO: Figure out what the right policy for read print jobs is.
-
             switch (printJob.getState()) {
-                case PrintJobInfo.STATE_QUEUED: {
-                    // Notify that we have a queued job.
-                    mService.onPrintJobQueued(new PrintJobInfo(printJob));
-                } break;
-
+                case PrintJobInfo.STATE_QUEUED:
                 case PrintJobInfo.STATE_STARTED: {
-                    // We really want to restart this print job.
-                    setPrintJobState(printJob.getId(), PrintJobInfo.STATE_QUEUED, null);
+                    // We have a print job that was queued or started in the past
+                    // but the device battery died or a crash occurred. In this case
+                    // we assume the print job failed and let the user decide whether
+                    // to restart the job or just
+                    setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
+                            mService.getString(R.string.no_connection_to_printer));
                 } break;
             }
         }