summaryrefslogtreecommitdiffstats
path: root/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
diff options
context:
space:
mode:
Diffstat (limited to 'packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java')
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java1225
1 files changed, 828 insertions, 397 deletions
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index 1e1cc24..86c4f37 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -24,9 +24,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -34,24 +32,27 @@ import android.os.IBinder.DeathRecipient;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.UserHandle;
+import android.print.ILayoutResultCallback;
import android.print.IPrintDocumentAdapter;
import android.print.IPrinterDiscoveryObserver;
+import android.print.IWriteResultCallback;
import android.print.PageRange;
import android.print.PrintAttributes;
import android.print.PrintAttributes.MediaSize;
-import android.print.PrintDocumentAdapter.LayoutResultCallback;
-import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentAdapter;
import android.print.PrintDocumentInfo;
import android.print.PrintJobInfo;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.text.Editable;
import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Choreographer;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -64,10 +65,13 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
+import android.widget.Toast;
-import java.io.File;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -76,15 +80,31 @@ import java.util.regex.Pattern;
*/
public class PrintJobConfigActivity extends Activity {
- private static final boolean DEBUG = false;
+ private static final String LOG_TAG = "PrintJobConfigActivity";
- private static final String LOG_TAG = PrintJobConfigActivity.class.getSimpleName();
+ private static final boolean DEBUG = true && Build.IS_DEBUGGABLE;
- public static final String EXTRA_PRINTABLE = "printable";
- public static final String EXTRA_APP_ID = "appId";
- public static final String EXTRA_ATTRIBUTES = "attributes";
+ private static final boolean LIVE_PREVIEW_SUPPORTED = false;
+
+ public static final String EXTRA_PRINT_DOCUMENT_ADAPTER = "printDocumentAdapter";
+ public static final String EXTRA_PRINT_ATTRIBUTES = "printAttributes";
public static final String EXTRA_PRINT_JOB_ID = "printJobId";
+ private static final int CONTROLLER_STATE_INITIALIZED = 1;
+ private static final int CONTROLLER_STATE_STARTED = 2;
+ private static final int CONTROLLER_STATE_LAYOUT_STARTED = 3;
+ private static final int CONTROLLER_STATE_LAYOUT_COMPLETED = 4;
+ private static final int CONTROLLER_STATE_WRITE_STARTED = 5;
+ private static final int CONTROLLER_STATE_WRITE_COMPLETED = 6;
+ private static final int CONTROLLER_STATE_FINISHED = 7;
+ private static final int CONTROLLER_STATE_FAILED = 8;
+ private static final int CONTROLLER_STATE_CANCELLED = 9;
+
+ private static final int EDITOR_STATE_INITIALIZED = 1;
+ private static final int EDITOR_STATE_CONFIRMED_PRINT = 2;
+ private static final int EDITOR_STATE_CONFIRMED_PREVIEW = 3;
+ private static final int EDITOR_STATE_CANCELLED = 4;
+
private static final int MIN_COPIES = 1;
private static final Pattern PATTERN_DIGITS = Pattern.compile("\\d");
@@ -95,31 +115,12 @@ public class PrintJobConfigActivity extends Activity {
private static final Pattern PATTERN_PAGE_RANGE = Pattern.compile(
"([0-9]+[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*[,]?[\\s]*)+");
- private final PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this);
-
- private Handler mHandler;
-
- private Editor mEditor;
-
- private IPrinterDiscoveryObserver mPrinterDiscoveryObserver;
-
- private int mAppId;
- private int mPrintJobId;
+ public static final PageRange[] ALL_PAGES_ARRAY = new PageRange[] {PageRange.ALL_PAGES};
private final PrintAttributes mOldPrintAttributes = new PrintAttributes.Builder().create();
private final PrintAttributes mCurrPrintAttributes = new PrintAttributes.Builder().create();
private final PrintAttributes mTempPrintAttributes = new PrintAttributes.Builder().create();
- private RemotePrintDocumentAdapter mRemotePrintAdapter;
-
- private boolean mPrintConfirmed;
-
- private boolean mStarted;
-
- private IBinder mIPrintDocumentAdapter;
-
- private PrintDocumentInfo mPrintDocumentInfo;
-
private final DeathRecipient mDeathRecipient = new DeathRecipient() {
@Override
public void binderDied() {
@@ -127,369 +128,484 @@ public class PrintJobConfigActivity extends Activity {
}
};
- @Override
- protected void onDestroy() {
- mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
- super.onDestroy();
- }
+ private PrintSpooler mSpooler;
+ private Editor mEditor;
+ private Document mDocument;
+ private PrintController mController;
+ private PrinterDiscoveryObserver mPrinterDiscoveryObserver;
+
+ private int mPrintJobId;
+
+ private IBinder mIPrintDocumentAdapter;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.print_job_config_activity);
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN
- | WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+ getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
+
+ Bundle extras = getIntent().getExtras();
+
+ mPrintJobId = extras.getInt(EXTRA_PRINT_JOB_ID, -1);
+ if (mPrintJobId < 0) {
+ throw new IllegalArgumentException("Invalid print job id: " + mPrintJobId);
+ }
+
+ mIPrintDocumentAdapter = extras.getBinder(EXTRA_PRINT_DOCUMENT_ADAPTER);
+ if (mIPrintDocumentAdapter == null) {
+ throw new IllegalArgumentException("PrintDocumentAdapter cannot be null");
+ }
+
+ PrintAttributes attributes = getIntent().getParcelableExtra(EXTRA_PRINT_ATTRIBUTES);
+ if (attributes != null) {
+ mCurrPrintAttributes.copyFrom(attributes);
+ }
- mHandler = new MyHandler(Looper.getMainLooper());
+ mSpooler = PrintSpooler.getInstance(this);
mEditor = new Editor();
+ mDocument = new Document();
+ mController = new PrintController(new RemotePrintDocumentAdapter(
+ IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter),
+ mSpooler.generateFileForPrintJob(mPrintJobId)));
}
@Override
protected void onResume() {
super.onResume();
- mPrintSpooler.startPrinterDiscovery(mPrinterDiscoveryObserver);
- notifyPrintableStartIfNeeded();
+ try {
+ mIPrintDocumentAdapter.linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException re) {
+ finish();
+ return;
+ }
+ mController.initialize();
+ mEditor.initialize();
+ mPrinterDiscoveryObserver = new PrinterDiscoveryObserver(mEditor, getMainLooper());
+ mSpooler.startPrinterDiscovery(mPrinterDiscoveryObserver);
}
@Override
protected void onPause() {
+ mSpooler.stopPrinterDiscovery();
+ mPrinterDiscoveryObserver.destroy();
+ mPrinterDiscoveryObserver = null;
+ if (mController.isCancelled() || mController.isFailed()) {
+ mSpooler.setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_CANCELED);
+ } else if (mController.hasStarted()) {
+ mController.finish();
+ if (mEditor.isPrintConfirmed()) {
+ if (mController.isFinished()) {
+ mSpooler.setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_QUEUED);
+ } else {
+ mSpooler.setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_CANCELED);
+ }
+ }
+ }
+ mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
super.onPause();
- mPrintSpooler.stopPrinterDiscovery();
- notifyPrintableFinishIfNeeded();
}
- private void notifyPrintableStartIfNeeded() {
- if (mEditor.getCurrentPrinter() == null
- || mStarted) {
- return;
+ public boolean onTouchEvent(MotionEvent event) {
+ if (!mEditor.isPrintConfirmed() && !mEditor.isPreviewConfirmed()
+ && getWindow().shouldCloseOnTouch(this, event)) {
+ if (!mController.isWorking()) {
+ PrintJobConfigActivity.this.finish();
+ }
+ mEditor.cancel();
+ return true;
}
- mStarted = true;
- mRemotePrintAdapter.start();
+ return super.onTouchEvent(event);
}
- private void updatePrintableContentIfNeeded() {
- if (!mStarted) {
- return;
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ event.startTracking();
}
+ return super.onKeyDown(keyCode, event);
+ }
- mPrintSpooler.setPrintJobAttributes(mPrintJobId, mCurrPrintAttributes);
-
- mRemotePrintAdapter.cancel();
- mHandler.removeMessages(MyHandler.MSG_ON_LAYOUT_FINISHED);
- mHandler.removeMessages(MyHandler.MSG_ON_LAYOUT_FAILED);
-
- // TODO: Implement setting the print preview attribute
- mRemotePrintAdapter.layout(mOldPrintAttributes,
- mCurrPrintAttributes, new LayoutResultCallback() {
- @Override
- public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
- mHandler.obtainMessage(MyHandler.MSG_ON_LAYOUT_FINISHED, changed ? 1 : 0,
- 0, info).sendToTarget();
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
+ && !event.isCanceled()) {
+ if (!mController.isWorking()) {
+ PrintJobConfigActivity.this.finish();
}
+ mEditor.cancel();
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
- @Override
- public void onLayoutFailed(CharSequence error) {
- mHandler.obtainMessage(MyHandler.MSG_ON_LAYOUT_FAILED, error).sendToTarget();
- }
- }, new Bundle());
+ private boolean printAttributesChanged() {
+ return !mOldPrintAttributes.equals(mCurrPrintAttributes);
}
- private void handleOnLayoutFinished(PrintDocumentInfo info, boolean changed) {
- mPrintDocumentInfo = info;
+ private class PrintController {
+ private final AtomicInteger mRequestCounter = new AtomicInteger();
- mEditor.updateUiIfNeeded();
+ private final RemotePrintDocumentAdapter mRemotePrintAdapter;
- // TODO: Handle the case of unchanged content
- mPrintSpooler.setPrintJobPrintDocumentInfo(mPrintJobId, info);
+ private final Handler mHandler;
- // TODO: Implement page selector.
- final List<PageRange> pages = new ArrayList<PageRange>();
- pages.add(PageRange.ALL_PAGES);
+ private int mControllerState = CONTROLLER_STATE_INITIALIZED;
- mRemotePrintAdapter.write(pages, new WriteResultCallback() {
+ private PageRange[] mRequestedPages;
+
+ private Bundle mMetadata = new Bundle();
+
+ private final ILayoutResultCallback mILayoutResultCallback =
+ new ILayoutResultCallback.Stub() {
@Override
- public void onWriteFinished(List<PageRange> pages) {
- mHandler.obtainMessage(MyHandler.MSG_ON_WRITE_FINISHED, pages).sendToTarget();
+ 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 onWriteFailed(CharSequence error) {
- mHandler.obtainMessage(MyHandler.MSG_ON_WRITE_FAILED, error).sendToTarget();
+ public void onLayoutFailed(CharSequence error, int sequence) {
+ if (mRequestCounter.get() == sequence) {
+ mHandler.obtainMessage(MyHandler.MSG_ON_LAYOUT_FAILED, error).sendToTarget();
+ }
}
- });
- }
-
- private void handleOnLayoutFailed(CharSequence error) {
- Log.e(LOG_TAG, "Error during layout: " + error);
- finishActivity(Activity.RESULT_CANCELED);
- }
+ };
- private void handleOnWriteFinished(List<PageRange> pages) {
- // TODO: Now we have to allow the preview button
- mEditor.updatePrintPreview(mRemotePrintAdapter.getFile());
- }
+ 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 void handleOnWriteFailed(CharSequence error) {
- Log.e(LOG_TAG, "Error write layout: " + error);
- finishActivity(Activity.RESULT_CANCELED);
- }
+ @Override
+ public void onWriteFailed(CharSequence error, int sequence) {
+ if (mRequestCounter.get() == sequence) {
+ mHandler.obtainMessage(MyHandler.MSG_ON_WRITE_FAILED, error).sendToTarget();
+ }
+ }
+ };
- private void notifyPrintableFinishIfNeeded() {
- if (!mStarted) {
- return;
+ public PrintController(RemotePrintDocumentAdapter adapter) {
+ mRemotePrintAdapter = adapter;
+ mHandler = new MyHandler(Looper.getMainLooper());
}
- if (!mPrintConfirmed) {
- mRemotePrintAdapter.cancel();
+ public void initialize() {
+ mControllerState = CONTROLLER_STATE_INITIALIZED;
}
- mRemotePrintAdapter.finish();
- PrinterInfo printer = mEditor.getCurrentPrinter();
- // If canceled or no printer, nothing to do.
- if (!mPrintConfirmed || printer == null) {
- // Update the print job's status.
- mPrintSpooler.setPrintJobState(mPrintJobId,
- PrintJobInfo.STATE_CANCELED);
- return;
+ public void cancel() {
+ mControllerState = CONTROLLER_STATE_CANCELLED;
}
- // Update the print job's printer.
- mPrintSpooler.setPrintJobPrinterId(mPrintJobId, printer.getId());
+ public boolean isCancelled() {
+ return (mControllerState == CONTROLLER_STATE_CANCELLED);
+ }
- // Update the print job's status.
- mPrintSpooler.setPrintJobState(mPrintJobId,
- PrintJobInfo.STATE_QUEUED);
+ public boolean isFinished() {
+ return (mControllerState == CONTROLLER_STATE_FINISHED);
+ }
- if (DEBUG) {
- if (mPrintConfirmed) {
- File file = mRemotePrintAdapter.getFile();
- if (file.exists()) {
- new ViewSpooledFileAsyncTask(file).executeOnExecutor(
- AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
- }
- }
+ public boolean isFailed() {
+ return (mControllerState == CONTROLLER_STATE_FAILED);
}
- }
- private boolean hasPdfViewer() {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setType("application/pdf");
- return !getPackageManager().queryIntentActivities(intent,
- PackageManager.MATCH_DEFAULT_ONLY).isEmpty();
- }
+ public boolean hasStarted() {
+ return mControllerState >= CONTROLLER_STATE_STARTED;
+ }
- // Caution: Use this only for debugging
- private final class ViewSpooledFileAsyncTask extends AsyncTask<Void, Void, Void> {
+ public boolean hasPerformedLayout() {
+ return mControllerState >= CONTROLLER_STATE_LAYOUT_COMPLETED;
+ }
- private final File mFile;
+ public boolean isWorking() {
+ return mControllerState == CONTROLLER_STATE_LAYOUT_STARTED
+ || mControllerState == CONTROLLER_STATE_WRITE_STARTED;
+ }
- public ViewSpooledFileAsyncTask(File file) {
- mFile = file;
+ public void start() {
+ mControllerState = CONTROLLER_STATE_STARTED;
+ mRemotePrintAdapter.start();
}
- @Override
- protected Void doInBackground(Void... params) {
- mFile.setExecutable(true, false);
- mFile.setWritable(true, false);
- mFile.setReadable(true, false);
+ public void update() {
+ if (!printAttributesChanged()) {
+ // 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);
+ } else {
+ mSpooler.setPrintJobAttributesNoPersistence(mPrintJobId, mCurrPrintAttributes);
- final long identity = Binder.clearCallingIdentity();
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setDataAndType(Uri.fromFile(mFile), "application/pdf");
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivityAsUser(intent, null, UserHandle.CURRENT);
- Binder.restoreCallingIdentity(identity);
- return null;
- }
- }
+ mMetadata.putBoolean(PrintDocumentAdapter.METADATA_KEY_PRINT_PREVIEW,
+ !mEditor.isPrintConfirmed());
- private final class PrintDiscoveryObserver extends IPrinterDiscoveryObserver.Stub {
- private static final int MESSAGE_ADD_DICOVERED_PRINTERS = 1;
- private static final int MESSAGE_REMOVE_DICOVERED_PRINTERS = 2;
+ mControllerState = CONTROLLER_STATE_LAYOUT_STARTED;
- private final Handler mHandler;
+ mRemotePrintAdapter.layout(mOldPrintAttributes, mCurrPrintAttributes,
+ mILayoutResultCallback, mMetadata, mRequestCounter.incrementAndGet());
- @SuppressWarnings("unchecked")
- public PrintDiscoveryObserver(Looper looper) {
- mHandler = new Handler(looper, null, true) {
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MESSAGE_ADD_DICOVERED_PRINTERS: {
- List<PrinterInfo> printers = (List<PrinterInfo>) message.obj;
- mEditor.addPrinters(printers);
- } break;
- case MESSAGE_REMOVE_DICOVERED_PRINTERS: {
- List<PrinterId> printerIds = (List<PrinterId>) message.obj;
- mEditor.removePrinters(printerIds);
- } break;
- }
- }
- };
+ mOldPrintAttributes.copyFrom(mCurrPrintAttributes);
+ }
}
- @Override
- public void addDiscoveredPrinters(List<PrinterInfo> printers) {
- mHandler.obtainMessage(MESSAGE_ADD_DICOVERED_PRINTERS, printers).sendToTarget();
+ public void finish() {
+ mControllerState = CONTROLLER_STATE_FINISHED;
+ mRemotePrintAdapter.finish();
}
- @Override
- public void removeDiscoveredPrinters(List<PrinterId> printers) {
- mHandler.obtainMessage(MESSAGE_REMOVE_DICOVERED_PRINTERS, printers).sendToTarget();
- }
- }
+ private void handleOnLayoutFinished(PrintDocumentInfo info, boolean layoutChanged) {
+ if (isCancelled()) {
+ if (mEditor.isDone()) {
+ PrintJobConfigActivity.this.finish();
+ }
+ return;
+ }
- private final class SpinnerItem<T> {
- final T value;
- CharSequence label;
+ mControllerState = CONTROLLER_STATE_LAYOUT_COMPLETED;
- public SpinnerItem(T value, CharSequence label) {
- this.value = value;
- this.label = label;
- }
+ // If the info changed, we update the document and the print job,
+ // and update the UI since the the page range selection may have
+ // become invalid.
+ final boolean infoChanged = !info.equals(mDocument.info);
+ if (infoChanged) {
+ mDocument.info = info;
+ mSpooler.setPrintJobPrintDocumentInfoNoPersistence(mPrintJobId, info);
+ mEditor.updateUi();
+ }
- public String toString() {
- return label.toString();
- }
- }
+ // If the document info or the layout changed, then
+ // drop the pages since we have to fetch them again.
+ if (infoChanged || layoutChanged) {
+ mDocument.pages = null;
+ }
- /**
- * An instance of this class class is intended to be the first focusable
- * in a layout to which the system automatically gives focus. It performs
- * some voodoo to avoid the first tap on it to start an edit mode, rather
- * to bring up the IME, i.e. to get the behavior as if the view was not
- * focused.
- */
- public static final class CustomEditText extends EditText {
- private boolean mClickedBeforeFocus;
+ // No pages means that the user selected an invalid range while we
+ // were doing a layout or the layout returned a document info for
+ // which the selected range is invalid. In such a case we do not
+ // write anything and wait for the user to fix the range which will
+ // trigger an update.
+ mRequestedPages = mEditor.getRequestedPages();
+ if (mRequestedPages == null) {
+ if (mEditor.isDone()) {
+ PrintJobConfigActivity.this.finish();
+ }
+ return;
+ }
- public CustomEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
+ // If the info and the layout did not change and we already have
+ // the requested pages, then nothing else to do.
+ if (!infoChanged && !layoutChanged
+ && PageRangeUtils.contains(mDocument.pages, mRequestedPages)) {
+ if (mEditor.isDone()) {
+ PrintJobConfigActivity.this.finish();
+ }
+ return;
+ }
- @Override
- public boolean performClick() {
- super.performClick();
- if (isFocused() && !mClickedBeforeFocus) {
- clearFocus();
- requestFocus();
+ // If we do not support live preview and the current layout is
+ // not for preview purposes, i.e. the user did not poke the
+ // preview button, then just skip the write.
+ if (!LIVE_PREVIEW_SUPPORTED && !mEditor.isPreviewConfirmed()
+ && mMetadata.getBoolean(PrintDocumentAdapter.METADATA_KEY_PRINT_PREVIEW)) {
+ if (mEditor.isDone()) {
+ PrintJobConfigActivity.this.finish();
+ }
+ return;
}
- mClickedBeforeFocus = true;
- return true;
+
+ // Request a write of the pages of interest.
+ mControllerState = CONTROLLER_STATE_WRITE_STARTED;
+ mRemotePrintAdapter.write(mRequestedPages, mIWriteResultCallback,
+ mRequestCounter.incrementAndGet());
}
- @Override
- public void setError(CharSequence error, Drawable icon) {
- setCompoundDrawables(null, null, icon, null);
+ private void handleOnLayoutFailed(CharSequence error) {
+ mControllerState = CONTROLLER_STATE_FAILED;
+ // TODO: We need some UI for announcing an error.
+ Log.e(LOG_TAG, "Error during layout: " + error);
+ PrintJobConfigActivity.this.finish();
}
- protected void onFocusChanged(boolean gainFocus, int direction,
- Rect previouslyFocusedRect) {
- if (!gainFocus) {
- mClickedBeforeFocus = false;
+ private void handleOnWriteFinished(PageRange[] pages) {
+ if (isCancelled()) {
+ if (mEditor.isDone()) {
+ PrintJobConfigActivity.this.finish();
+ }
+ return;
}
- super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
- }
- }
- private final class MyHandler 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;
+ mControllerState = CONTROLLER_STATE_WRITE_COMPLETED;
+
+ // Update which pages we have fetched.
+ mDocument.pages = PageRangeUtils.normalize(pages);
- public MyHandler(Looper looper) {
- super(looper, null, false);
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Requested: " + Arrays.toString(mRequestedPages)
+ + " and got: " + Arrays.toString(mDocument.pages));
+ }
+
+ // Adjust the print job pages based on what was requested and written.
+ // The cases are ordered in the most expected to the least expected.
+ if (Arrays.equals(mDocument.pages, mRequestedPages)) {
+ // We got a document with exactly the pages we wanted. Hence,
+ // the printer has to print all pages in the data.
+ mSpooler.setPrintJobPagesNoPersistence(mPrintJobId, ALL_PAGES_ARRAY);
+ } else if (Arrays.equals(mDocument.pages, ALL_PAGES_ARRAY)) {
+ // We requested specific pages but got all of them. Hence,
+ // the printer has to print only the requested pages.
+ mSpooler.setPrintJobPagesNoPersistence(mPrintJobId, mRequestedPages);
+ } else if (PageRangeUtils.contains(mDocument.pages, mRequestedPages)) {
+ // We requested specific pages and got more but not all pages.
+ // Hence, we have to offset appropriately the printed pages to
+ // exclude the pages we did not request. Note that pages is
+ // guaranteed to be not null and not empty.
+ final int offset = mDocument.pages[0].getStart() - pages[0].getStart();
+ PageRange[] offsetPages = Arrays.copyOf(mDocument.pages, mDocument.pages.length);
+ PageRangeUtils.offsetStart(offsetPages, offset);
+ mSpooler.setPrintJobPagesNoPersistence(mPrintJobId, offsetPages);
+ } else if (Arrays.equals(mRequestedPages, ALL_PAGES_ARRAY)
+ && mDocument.pages.length == 1 && mDocument.pages[0].getStart() == 0
+ && mDocument.pages[0].getEnd() == mDocument.info.getPageCount() - 1) {
+ // We requested all pages via the special constant and got all
+ // of them as an explicit enumeration. Hence, the printer has
+ // to print only the requested pages.
+ mSpooler.setPrintJobPagesNoPersistence(mPrintJobId, mDocument.pages);
+ } 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");
+ PrintJobConfigActivity.this.finish();
+ }
+
+ if (mEditor.isDone()) {
+ PrintJobConfigActivity.this.finish();
+ }
}
- @Override
- @SuppressWarnings("unchecked")
- public void handleMessage(Message message) {
- switch (message.what) {
- case MSG_ON_LAYOUT_FINISHED: {
- PrintDocumentInfo info = (PrintDocumentInfo) message.obj;
- final boolean changed = (message.arg1 == 1);
- handleOnLayoutFinished(info, changed);
- } break;
-
- case MSG_ON_LAYOUT_FAILED: {
- CharSequence error = (CharSequence) message.obj;
- handleOnLayoutFailed(error);
- } break;
-
- case MSG_ON_WRITE_FINISHED: {
- List<PageRange> pages = (List<PageRange>) message.obj;
- handleOnWriteFinished(pages);
- } break;
-
- case MSG_ON_WRITE_FAILED: {
- CharSequence error = (CharSequence) message.obj;
- handleOnWriteFailed(error);
- } break;
+ private void handleOnWriteFailed(CharSequence error) {
+ mControllerState = CONTROLLER_STATE_FAILED;
+ Log.e(LOG_TAG, "Error during write: " + error);
+ PrintJobConfigActivity.this.finish();
+ }
+
+ private final class MyHandler 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) {
+ super(looper, null, false);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_ON_LAYOUT_FINISHED: {
+ PrintDocumentInfo info = (PrintDocumentInfo) message.obj;
+ final boolean changed = (message.arg1 == 1);
+ mController.handleOnLayoutFinished(info, changed);
+ } break;
+
+ case MSG_ON_LAYOUT_FAILED: {
+ CharSequence error = (CharSequence) message.obj;
+ mController.handleOnLayoutFailed(error);
+ } break;
+
+ case MSG_ON_WRITE_FINISHED: {
+ PageRange[] pages = (PageRange[]) message.obj;
+ mController.handleOnWriteFinished(pages);
+ } break;
+
+ case MSG_ON_WRITE_FAILED: {
+ CharSequence error = (CharSequence) message.obj;
+ mController.handleOnWriteFailed(error);
+ } break;
+ }
}
}
}
- private class Editor {
- private EditText mCopiesEditText;
+ private final class Editor {
+ private final EditText mCopiesEditText;
+
+ private final TextView mRangeTitle;
+ private final EditText mRangeEditText;
- private EditText mRangeEditText;
+ private final Spinner mDestinationSpinner;
+ private final ArrayAdapter<SpinnerItem<PrinterInfo>> mDestinationSpinnerAdapter;
- private Spinner mDestinationSpinner;
- public ArrayAdapter<SpinnerItem<PrinterInfo>> mDestinationSpinnerAdapter;
+ private final Spinner mMediaSizeSpinner;
+ private final ArrayAdapter<SpinnerItem<MediaSize>> mMediaSizeSpinnerAdapter;
- private Spinner mMediaSizeSpinner;
- public ArrayAdapter<SpinnerItem<MediaSize>> mMediaSizeSpinnerAdapter;
+ private final Spinner mColorModeSpinner;
+ private final ArrayAdapter<SpinnerItem<Integer>> mColorModeSpinnerAdapter;
- private Spinner mColorModeSpinner;
- public ArrayAdapter<SpinnerItem<Integer>> mColorModeSpinnerAdapter;
+ private final Spinner mOrientationSpinner;
+ private final ArrayAdapter<SpinnerItem<Integer>> mOrientationSpinnerAdapter;
- private Spinner mOrientationSpinner;
- public ArrayAdapter<SpinnerItem<Integer>> mOrientationSpinnerAdapter;
+ private final Spinner mRangeOptionsSpinner;
+ private final ArrayAdapter<SpinnerItem<Integer>> mRangeOptionsSpinnerAdapter;
- private Spinner mRangeOptionsSpinner;
- public ArrayAdapter<SpinnerItem<Integer>> mRangeOptionsSpinnerAdapter;
+ private final SimpleStringSplitter mStringCommaSplitter =
+ new SimpleStringSplitter(',');
- private Button mPrintPreviewButton;
+ private final Button mPrintPreviewButton;
- private Button mPrintButton;
+ private final Button mPrintButton;
private final OnItemSelectedListener mOnItemSelectedListener =
new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> spinner, View view, int position, long id) {
if (spinner == mDestinationSpinner) {
- mOldPrintAttributes.copyFrom(mCurrPrintAttributes);
mCurrPrintAttributes.clear();
- final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
- if (selectedIndex >= 0) {
- mDestinationSpinnerAdapter.getItem(selectedIndex).value.getDefaults(
- mCurrPrintAttributes);
+ SpinnerItem<PrinterInfo> dstItem = mDestinationSpinnerAdapter.getItem(position);
+ if (dstItem != null) {
+ mSpooler.setPrintJobPrinterIdNoPersistence(mPrintJobId, dstItem.value.getId());
+ dstItem.value.getDefaults(mCurrPrintAttributes);
+ }
+ updateUi();
+ if (!mController.hasStarted()) {
+ mController.start();
+ }
+ if (!hasErrors()) {
+ mController.update();
}
- updateUiIfNeeded();
- notifyPrintableStartIfNeeded();
- updatePrintableContentIfNeeded();
} else if (spinner == mMediaSizeSpinner) {
SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position);
- mOldPrintAttributes.copyFrom(mCurrPrintAttributes);
mCurrPrintAttributes.setMediaSize(mediaItem.value);
- updatePrintableContentIfNeeded();
+ if (!hasErrors()) {
+ mController.update();
+ }
} else if (spinner == mColorModeSpinner) {
SpinnerItem<Integer> colorModeItem =
mColorModeSpinnerAdapter.getItem(position);
- mOldPrintAttributes.copyFrom(mCurrPrintAttributes);
mCurrPrintAttributes.setColorMode(colorModeItem.value);
- updatePrintableContentIfNeeded();
+ if (!hasErrors()) {
+ mController.update();
+ }
} else if (spinner == mOrientationSpinner) {
SpinnerItem<Integer> orientationItem =
mOrientationSpinnerAdapter.getItem(position);
- mOldPrintAttributes.copyFrom(mCurrPrintAttributes);
mCurrPrintAttributes.setOrientation(orientationItem.value);
- updatePrintableContentIfNeeded();
+ if (!hasErrors()) {
+ mController.update();
+ }
} else if (spinner == mRangeOptionsSpinner) {
- updateUiIfNeeded();
- updatePrintableContentIfNeeded();
+ updateUi();
+ if (!hasErrors()) {
+ mController.update();
+ }
}
}
@@ -512,19 +628,28 @@ public class PrintJobConfigActivity extends Activity {
@Override
public void afterTextChanged(Editable editable) {
+ final boolean hadErrors = hasErrors();
+
if (editable.length() == 0) {
mCopiesEditText.setError("");
- mPrintButton.setEnabled(false);
+ updateUi();
return;
}
+
final int copies = Integer.parseInt(editable.toString());
if (copies < MIN_COPIES) {
mCopiesEditText.setError("");
- mPrintButton.setEnabled(false);
+ updateUi();
return;
}
- mOldPrintAttributes.copyFrom(mCurrPrintAttributes);
- mCurrPrintAttributes.setCopies(copies);
+
+ mCopiesEditText.setError(null);
+ mSpooler.setPrintJobCopiesNoPersistence(mPrintJobId, copies);
+ updateUi();
+
+ if (hadErrors && !hasErrors() && printAttributesChanged()) {
+ mController.update();
+ }
}
};
@@ -541,18 +666,20 @@ public class PrintJobConfigActivity extends Activity {
@Override
public void afterTextChanged(Editable editable) {
+ final boolean hadErrors = hasErrors();
+
String text = editable.toString();
if (TextUtils.isEmpty(text)) {
mRangeEditText.setError("");
- mPrintButton.setEnabled(false);
+ updateUi();
return;
}
String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////");
if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) {
mRangeEditText.setError("");
- mPrintButton.setEnabled(false);
+ updateUi();
return;
}
@@ -560,100 +687,36 @@ public class PrintJobConfigActivity extends Activity {
while (matcher.find()) {
String numericString = text.substring(matcher.start(), matcher.end());
final int pageIndex = Integer.parseInt(numericString);
- if (pageIndex < 1 || pageIndex > mPrintDocumentInfo.getPageCount()) {
+ if (pageIndex < 1 || pageIndex > mDocument.info.getPageCount()) {
mRangeEditText.setError("");
- mPrintButton.setEnabled(false);
+ updateUi();
return;
}
}
+ //TODO: Catch the error if start is less grater than the end.
+
mRangeEditText.setError(null);
mPrintButton.setEnabled(true);
- }
- };
-
- public Editor() {
- Bundle extras = getIntent().getExtras();
-
- mPrintJobId = extras.getInt(EXTRA_PRINT_JOB_ID, -1);
- if (mPrintJobId < 0) {
- throw new IllegalArgumentException("Invalid print job id: " + mPrintJobId);
- }
-
- mAppId = extras.getInt(EXTRA_APP_ID, -1);
- if (mAppId < 0) {
- throw new IllegalArgumentException("Invalid app id: " + mAppId);
- }
-
- PrintAttributes attributes = getIntent().getParcelableExtra(EXTRA_ATTRIBUTES);
- if (attributes == null) {
- mCurrPrintAttributes.copyFrom(attributes);
- }
+ updateUi();
- mIPrintDocumentAdapter = extras.getBinder(EXTRA_PRINTABLE);
- if (mIPrintDocumentAdapter == null) {
- throw new IllegalArgumentException("Printable cannot be null");
- }
- mRemotePrintAdapter = new RemotePrintDocumentAdapter(
- IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter),
- mPrintSpooler.generateFileForPrintJob(mPrintJobId));
-
- try {
- mIPrintDocumentAdapter.linkToDeath(mDeathRecipient, 0);
- } catch (RemoteException re) {
- finish();
+ if (hadErrors && !hasErrors() && printAttributesChanged()) {
+ updateUi();
+ }
}
+ };
- mPrinterDiscoveryObserver = new PrintDiscoveryObserver(getMainLooper());
-
- bindUi();
- }
+ private int mEditorState;
- private void bindUi() {
+ public Editor() {
// Copies
mCopiesEditText = (EditText) findViewById(R.id.copies_edittext);
- mCopiesEditText.setText(String.valueOf(MIN_COPIES));
mCopiesEditText.addTextChangedListener(mCopiesTextWatcher);
- mCopiesEditText.setText(String.valueOf(
- Math.max(mCurrPrintAttributes.getCopies(), MIN_COPIES)));
mCopiesEditText.selectAll();
// Destination.
mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner);
- mDestinationSpinnerAdapter = new ArrayAdapter<SpinnerItem<PrinterInfo>>(
- PrintJobConfigActivity.this, R.layout.spinner_dropdown_item) {
- @Override
- public View getDropDownView(int position, View convertView,
- ViewGroup parent) {
- return getView(position, convertView, parent);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = getLayoutInflater().inflate(
- R.layout.spinner_dropdown_item, parent, false);
- }
-
- PrinterInfo printerInfo = getItem(position).value;
- TextView title = (TextView) convertView.findViewById(R.id.title);
- title.setText(printerInfo.getLabel());
-
- try {
- TextView subtitle = (TextView)
- convertView.findViewById(R.id.subtitle);
- PackageManager pm = getPackageManager();
- PackageInfo packageInfo = pm.getPackageInfo(
- printerInfo.getId().getService().getPackageName(), 0);
- subtitle.setText(packageInfo.applicationInfo.loadLabel(pm));
- subtitle.setVisibility(View.VISIBLE);
- } catch (NameNotFoundException nnfe) {
- /* ignore */
- }
-
- return convertView;
- }
- };
+ mDestinationSpinnerAdapter = new DestinationAdapter();
mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter);
mDestinationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
@@ -682,6 +745,7 @@ public class PrintJobConfigActivity extends Activity {
mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
// Range
+ mRangeTitle = (TextView) findViewById(R.id.page_range_title);
mRangeEditText = (EditText) findViewById(R.id.page_range_edittext);
mRangeEditText.addTextChangedListener(mRangeTextWatcher);
@@ -690,8 +754,6 @@ public class PrintJobConfigActivity extends Activity {
mRangeOptionsSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(
PrintJobConfigActivity.this,
R.layout.spinner_dropdown_item, R.id.title);
- mRangeOptionsSpinner.setAdapter(mRangeOptionsSpinnerAdapter);
- mRangeOptionsSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
final int[] rangeOptionsValues = getResources().getIntArray(
R.array.page_options_values);
String[] rangeOptionsLabels = getResources().getStringArray(
@@ -701,13 +763,29 @@ public class PrintJobConfigActivity extends Activity {
mRangeOptionsSpinnerAdapter.add(new SpinnerItem<Integer>(
rangeOptionsValues[i], rangeOptionsLabels[i]));
}
+ mRangeOptionsSpinner.setAdapter(mRangeOptionsSpinnerAdapter);
mRangeOptionsSpinner.setSelection(0);
+ // Here is some voodoo to circumvent the weird behavior of AdapterView
+ // in which a selection listener may get a callback for an event that
+ // happened before the listener was registered. The reason for that is
+ // that the selection change is handled on the next layout pass.
+ Choreographer.getInstance().postCallbackDelayed(Choreographer.CALLBACK_TRAVERSAL,
+ new Runnable() {
+ @Override
+ public void run() {
+ mRangeOptionsSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+ }
+ }, null, Choreographer.getFrameDelay() * 2);
mPrintPreviewButton = (Button) findViewById(R.id.print_preview_button);
mPrintPreviewButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- // TODO: Implement
+ mEditor.confirmPreview();
+ // TODO: Implement me
+ Toast.makeText(PrintJobConfigActivity.this,
+ "Stop poking me! Not implemented yet :)",
+ Toast.LENGTH_LONG).show();
}
});
@@ -715,21 +793,106 @@ public class PrintJobConfigActivity extends Activity {
mPrintButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- mPrintConfirmed = true;
- finish();
+ mEditor.confirmPrint();
+ updateUi();
+ mController.update();
}
});
}
- private void updateUiIfNeeded() {
+ public void initialize() {
+ mEditorState = EDITOR_STATE_INITIALIZED;
+ mDestinationSpinner.setSelection(AdapterView.INVALID_POSITION);
+ }
+
+ public boolean isCancelled() {
+ return mEditorState == EDITOR_STATE_CANCELLED;
+ }
+
+ public void cancel() {
+ mEditorState = EDITOR_STATE_CANCELLED;
+ mController.cancel();
+ updateUi();
+ }
+
+ public boolean isDone() {
+ return isPrintConfirmed() || isPreviewConfirmed() || isCancelled();
+ }
+
+ public boolean isPrintConfirmed() {
+ return mEditorState == EDITOR_STATE_CONFIRMED_PRINT;
+ }
+
+ public void confirmPrint() {
+ mEditorState = EDITOR_STATE_CONFIRMED_PRINT;
+ }
+
+ public boolean isPreviewConfirmed() {
+ return mEditorState == EDITOR_STATE_CONFIRMED_PRINT;
+ }
+
+ public void confirmPreview() {
+ mEditorState = EDITOR_STATE_CONFIRMED_PREVIEW;
+ }
+
+ public PageRange[] getRequestedPages() {
+ if (hasErrors()) {
+ return null;
+ }
+ if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) {
+ List<PageRange> pageRanges = new ArrayList<PageRange>();
+ mStringCommaSplitter.setString(mRangeEditText.getText().toString());
+
+ while (mStringCommaSplitter.hasNext()) {
+ String range = mStringCommaSplitter.next().trim();
+ final int dashIndex = range.indexOf('-');
+ final int fromIndex;
+ final int toIndex;
+
+ if (dashIndex > 0) {
+ fromIndex = Integer.parseInt(range.substring(0, dashIndex)) - 1;
+ toIndex = Integer.parseInt(range.substring(
+ dashIndex + 1, range.length())) - 1;
+ } else {
+ fromIndex = toIndex = Integer.parseInt(range);
+ }
+
+ PageRange pageRange = new PageRange(fromIndex, toIndex);
+ pageRanges.add(pageRange);
+ }
+
+ PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
+ pageRanges.toArray(pageRangesArray);
+
+ return PageRangeUtils.normalize(pageRangesArray);
+ }
+
+ return ALL_PAGES_ARRAY;
+ }
+
+ public void updateUi() {
+ if (isPrintConfirmed() || isPreviewConfirmed() || isCancelled()) {
+ mDestinationSpinner.setEnabled(false);
+ mCopiesEditText.setEnabled(false);
+ mMediaSizeSpinner.setEnabled(false);
+ mColorModeSpinner.setEnabled(false);
+ mOrientationSpinner.setEnabled(false);
+ mRangeOptionsSpinner.setEnabled(false);
+ mRangeEditText.setEnabled(false);
+ mPrintPreviewButton.setEnabled(false);
+ mPrintButton.setEnabled(false);
+ return;
+ }
+
final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
if (selectedIndex < 0) {
// Destination
mDestinationSpinner.setEnabled(false);
- // Copies
- mCopiesEditText.setText("1");
+ mCopiesEditText.removeTextChangedListener(mCopiesTextWatcher);
+ mCopiesEditText.setText(String.valueOf(MIN_COPIES));
+ mCopiesEditText.addTextChangedListener(mCopiesTextWatcher);
mCopiesEditText.setEnabled(false);
// Media size
@@ -751,7 +914,11 @@ public class PrintJobConfigActivity extends Activity {
mRangeOptionsSpinner.setOnItemSelectedListener(null);
mRangeOptionsSpinner.setSelection(0);
mRangeOptionsSpinner.setEnabled(false);
+ mRangeTitle.setText(getString(R.string.label_pages,
+ getString(R.string.page_count_unknown)));
+ mRangeEditText.removeTextChangedListener(mRangeTextWatcher);
mRangeEditText.setText("");
+ mRangeEditText.addTextChangedListener(mRangeTextWatcher);
mRangeEditText.setEnabled(false);
mRangeEditText.setVisibility(View.INVISIBLE);
@@ -884,46 +1051,65 @@ public class PrintJobConfigActivity extends Activity {
}
// Range options
- if (mPrintDocumentInfo != null && (mPrintDocumentInfo.getPageCount() > 1
- || mPrintDocumentInfo.getPageCount()
- == PrintDocumentInfo.PAGE_COUNT_UNKNOWN)) {
+ PrintDocumentInfo info = mDocument.info;
+ if (info != null && (info.getPageCount() > 1
+ || info.getPageCount() == PrintDocumentInfo.PAGE_COUNT_UNKNOWN)) {
mRangeOptionsSpinner.setEnabled(true);
if (mRangeOptionsSpinner.getSelectedItemPosition() > 0
&& !mRangeEditText.isEnabled()) {
mRangeEditText.setEnabled(true);
- mRangeEditText.setError("");
mRangeEditText.setVisibility(View.VISIBLE);
mRangeEditText.requestFocus();
InputMethodManager imm = (InputMethodManager)
getSystemService(INPUT_METHOD_SERVICE);
imm.showSoftInput(mRangeEditText, 0);
}
+ final int pageCount = mDocument.info.getPageCount();
+ mRangeTitle.setText(getString(R.string.label_pages,
+ (pageCount == PrintDocumentInfo.PAGE_COUNT_UNKNOWN)
+ ? getString(R.string.page_count_unknown)
+ : String.valueOf(pageCount)));
} else {
mRangeOptionsSpinner.setOnItemSelectedListener(null);
mRangeOptionsSpinner.setSelection(0);
mRangeOptionsSpinner.setEnabled(false);
+ mRangeTitle.setText(getString(R.string.label_pages,
+ getString(R.string.page_count_unknown)));
mRangeEditText.setEnabled(false);
- mRangeEditText.setText("");
mRangeEditText.setVisibility(View.INVISIBLE);
}
- // Print preview
- mPrintPreviewButton.setEnabled(true);
- if (hasPdfViewer()) {
- mPrintPreviewButton.setText(getString(R.string.print_preview));
+ // Print/Print preview
+ if ((mRangeOptionsSpinner.getSelectedItemPosition() == 1
+ && (TextUtils.isEmpty(mRangeEditText.getText()) || hasErrors()))
+ || (mRangeOptionsSpinner.getSelectedItemPosition() == 0
+ && (!mController.hasPerformedLayout() || hasErrors()))) {
+ mPrintPreviewButton.setEnabled(false);
+ mPrintButton.setEnabled(false);
} else {
- mPrintPreviewButton.setText(getString(R.string.install_for_print_preview));
+ mPrintPreviewButton.setEnabled(true);
+ if (hasPdfViewer()) {
+ mPrintPreviewButton.setText(getString(R.string.print_preview));
+ } else {
+ mPrintPreviewButton.setText(getString(R.string.install_for_print_preview));
+ }
+ mPrintButton.setEnabled(true);
}
- // Print
- mPrintButton.setEnabled(true);
+ // Copies
+ if (mCopiesEditText.getError() == null
+ && TextUtils.isEmpty(mCopiesEditText.getText())) {
+ mCopiesEditText.setText(String.valueOf(MIN_COPIES));
+ mCopiesEditText.selectAll();
+ mCopiesEditText.requestFocus();
+ }
}
// Here is some voodoo to circumvent the weird behavior of AdapterView
// in which a selection listener may get a callback for an event that
// happened before the listener was registered. The reason for that is
// that the selection change is handled on the next layout pass.
- Choreographer.getInstance().postCallback(Choreographer.CALLBACK_TRAVERSAL,
+ Choreographer.getInstance().postCallbackDelayed(Choreographer.CALLBACK_TRAVERSAL,
new Runnable() {
@Override
public void run() {
@@ -932,15 +1118,7 @@ public class PrintJobConfigActivity extends Activity {
mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
mRangeOptionsSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
}
- }, null);
- }
-
- public PrinterInfo getCurrentPrinter() {
- final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
- if (selectedIndex >= 0) {
- return mDestinationSpinnerAdapter.getItem(selectedIndex).value;
- }
- return null;
+ }, null, Choreographer.getFrameDelay() * 2);
}
public void addPrinters(List<PrinterInfo> addedPrinters) {
@@ -995,8 +1173,261 @@ public class PrintJobConfigActivity extends Activity {
}
}
- private void updatePrintPreview(File file) {
- // TODO: Implement
+ private boolean hasErrors() {
+ return mRangeEditText.getError() != null
+ || mCopiesEditText.getError() != null;
+ }
+
+ private boolean hasPdfViewer() {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setType("application/pdf");
+ return !getPackageManager().queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY).isEmpty();
+ }
+
+ private final class SpinnerItem<T> {
+ final T value;
+ CharSequence label;
+
+ public SpinnerItem(T value, CharSequence label) {
+ this.value = value;
+ this.label = label;
+ }
+
+ public String toString() {
+ return label.toString();
+ }
+ }
+
+ private final class DestinationAdapter extends ArrayAdapter<SpinnerItem<PrinterInfo>> {
+
+ public DestinationAdapter() {
+ super( PrintJobConfigActivity.this, R.layout.spinner_dropdown_item);
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView,
+ ViewGroup parent) {
+ return getView(position, convertView, parent);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(
+ R.layout.spinner_dropdown_item, parent, false);
+ }
+
+ PrinterInfo printerInfo = getItem(position).value;
+ TextView title = (TextView) convertView.findViewById(R.id.title);
+ title.setText(printerInfo.getLabel());
+
+ try {
+ TextView subtitle = (TextView)
+ convertView.findViewById(R.id.subtitle);
+ PackageManager pm = getPackageManager();
+ PackageInfo packageInfo = pm.getPackageInfo(
+ printerInfo.getId().getService().getPackageName(), 0);
+ subtitle.setText(packageInfo.applicationInfo.loadLabel(pm));
+ subtitle.setVisibility(View.VISIBLE);
+ } catch (NameNotFoundException nnfe) {
+ /* ignore */
+ }
+
+ return convertView;
+ }
+ }
+ }
+
+ private static final class PrinterDiscoveryObserver extends IPrinterDiscoveryObserver.Stub {
+ private static final int MESSAGE_ADD_DICOVERED_PRINTERS = 1;
+ private static final int MESSAGE_REMOVE_DICOVERED_PRINTERS = 2;
+
+ private Handler mHandler;
+ private Editor mEditor;
+
+ @SuppressWarnings("unchecked")
+ public PrinterDiscoveryObserver(Editor editor, Looper looper) {
+ mEditor = editor;
+ mHandler = new Handler(looper, null, true) {
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MESSAGE_ADD_DICOVERED_PRINTERS: {
+ List<PrinterInfo> printers = (List<PrinterInfo>) message.obj;
+ mEditor.addPrinters(printers);
+ } break;
+ case MESSAGE_REMOVE_DICOVERED_PRINTERS: {
+ List<PrinterId> printerIds = (List<PrinterId>) message.obj;
+ mEditor.removePrinters(printerIds);
+ } break;
+ }
+ }
+ };
+ }
+
+ @Override
+ public void addDiscoveredPrinters(List<PrinterInfo> printers) {
+ synchronized (this) {
+ if (mHandler != null) {
+ mHandler.obtainMessage(MESSAGE_ADD_DICOVERED_PRINTERS, printers)
+ .sendToTarget();
+ }
+ }
+ }
+
+ @Override
+ public void removeDiscoveredPrinters(List<PrinterId> printers) {
+ synchronized (this) {
+ if (mHandler != null) {
+ mHandler.obtainMessage(MESSAGE_REMOVE_DICOVERED_PRINTERS, printers)
+ .sendToTarget();
+ }
+ }
+ }
+
+ public void destroy() {
+ synchronized (this) {
+ mHandler = null;
+ mEditor = null;
+ }
+ }
+ }
+
+ /**
+ * An instance of this class class is intended to be the first focusable
+ * in a layout to which the system automatically gives focus. It performs
+ * some voodoo to avoid the first tap on it to start an edit mode, rather
+ * to bring up the IME, i.e. to get the behavior as if the view was not
+ * focused.
+ */
+ public static final class CustomEditText extends EditText {
+ private boolean mClickedBeforeFocus;
+ private CharSequence mError;
+
+ public CustomEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public boolean performClick() {
+ super.performClick();
+ if (isFocused() && !mClickedBeforeFocus) {
+ clearFocus();
+ requestFocus();
+ }
+ mClickedBeforeFocus = true;
+ return true;
+ }
+
+ @Override
+ public CharSequence getError() {
+ return mError;
+ }
+
+ @Override
+ public void setError(CharSequence error, Drawable icon) {
+ setCompoundDrawables(null, null, icon, null);
+ mError = error;
+ }
+
+ protected void onFocusChanged(boolean gainFocus, int direction,
+ Rect previouslyFocusedRect) {
+ if (!gainFocus) {
+ mClickedBeforeFocus = false;
+ }
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ }
+ }
+
+ private static final class Document {
+ public PrintDocumentInfo info;
+ public PageRange[] pages;
+ }
+
+ private static final class PageRangeUtils {
+
+ private static final Comparator<PageRange> sComparator = new Comparator<PageRange>() {
+ @Override
+ public int compare(PageRange lhs, PageRange rhs) {
+ return lhs.getStart() - rhs.getStart();
+ }
+ };
+
+ private PageRangeUtils() {
+ throw new UnsupportedOperationException();
+ }
+
+ public static boolean contains(PageRange[] ourPageRanges, PageRange[] otherPageRanges) {
+ if (ourPageRanges == null || otherPageRanges == null) {
+ return false;
+ }
+
+ otherPageRanges = normalize(otherPageRanges);
+
+ int otherPageIdx = 0;
+ final int myPageCount = ourPageRanges.length;
+ final int otherPageCount = otherPageRanges.length;
+ for (int i= 0; i < myPageCount; i++) {
+ PageRange myPage = ourPageRanges[i];
+ for (; otherPageIdx < otherPageCount; otherPageIdx++) {
+ PageRange otherPage = otherPageRanges[otherPageIdx];
+ if (otherPage.getStart() > myPage.getStart()) {
+ break;
+ }
+ if ((otherPage.getStart() < myPage.getStart()
+ && otherPage.getEnd() > myPage.getStart())
+ || (otherPage.getEnd() > myPage.getEnd()
+ && otherPage.getStart() < myPage.getEnd())
+ || (otherPage.getEnd() < myPage.getStart())) {
+ return false;
+ }
+ }
+ }
+ if (otherPageIdx < otherPageCount) {
+ return false;
+ }
+ return true;
+ }
+
+ public static PageRange[] normalize(PageRange[] pageRanges) {
+ if (pageRanges == null) {
+ return null;
+ }
+ final int oldPageCount = pageRanges.length;
+ if (oldPageCount <= 1) {
+ return pageRanges;
+ }
+ Arrays.sort(pageRanges, sComparator);
+ int newRangeCount = 0;
+ for (int i = 0; i < oldPageCount - 1; i++) {
+ newRangeCount++;
+ PageRange currentRange = pageRanges[i];
+ PageRange nextRange = pageRanges[i + 1];
+ if (currentRange.getEnd() >= nextRange.getStart()) {
+ newRangeCount--;
+ pageRanges[i] = null;
+ pageRanges[i + 1] = new PageRange(currentRange.getStart(),
+ nextRange.getEnd());
+ }
+ }
+ if (newRangeCount == oldPageCount) {
+ return pageRanges;
+ }
+ return Arrays.copyOfRange(pageRanges, oldPageCount - newRangeCount,
+ oldPageCount - 1);
+ }
+
+ public static void offsetStart(PageRange[] pageRanges, int offset) {
+ if (offset == 0) {
+ return;
+ }
+ final int pageRangeCount = pageRanges.length;
+ for (int i = 0; i < pageRangeCount; i++) {
+ final int start = pageRanges[i].getStart() + offset;
+ final int end = pageRanges[i].getEnd() + offset;
+ pageRanges[i] = new PageRange(start, end);
+ }
}
}
}