From 0d1daa50f6d180c57f92596501e2e5c0b5ef9997 Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Tue, 23 Jul 2013 13:29:31 -0700 Subject: [PATCH] Updating the print dialog and its interactinos with the printing app. 1. Added support for reporting the old print attributes during layout. Now we keep track of the old print attributes, so the app can compute the delta and decide whether re-layout work is needed. 2. Fixed PrintDocumentAdapter callback interleavings. Layout callbacks were intermixing with write ones - a mess. Now we make an attempt to cancel layout and write if they respond to cancellation, otherwise we wait but do not interleave them. 3. Refactored the PrintJobConfigActivity for easier maintenance and to have a single update UI method that does the minimal amount of work. Change-Id: I31ada1a0550882e6185018e6f17f923aed165d15 --- core/java/android/print/PrintAttributes.java | 16 + .../res/layout/print_job_config_activity.xml | 441 ++++---- packages/PrintSpooler/res/values/constants.xml | 4 +- packages/PrintSpooler/res/values/strings.xml | 10 +- .../printspooler/PrintJobConfigActivity.java | 1118 ++++++++++++-------- .../printspooler/RemotePrintDocumentAdapter.java | 153 ++- 6 files changed, 1081 insertions(+), 661 deletions(-) diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java index 65f1330a5858..87d75c0dfbee 100644 --- a/core/java/android/print/PrintAttributes.java +++ b/core/java/android/print/PrintAttributes.java @@ -384,6 +384,22 @@ public final class PrintAttributes implements Parcelable { } /** + * @hide + */ + public void copyFrom(PrintAttributes other) { + mMediaSize = other.mMediaSize; + mResolution = other.mResolution; + mMargins = other.mMargins; + mInputTray = other.mInputTray; + mOutputTray = other.mOutputTray; + mDuplexMode = other.mDuplexMode; + mColorMode = other.mColorMode; + mFittingMode = other.mFittingMode; + mOrientation = other.mOrientation; + mCopies = other.mCopies; + } + + /** * This class specifies a supported media size. */ public static final class MediaSize { diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity.xml b/packages/PrintSpooler/res/layout/print_job_config_activity.xml index 8736bdd97f8c..32bc15aea164 100644 --- a/packages/PrintSpooler/res/layout/print_job_config_activity.xml +++ b/packages/PrintSpooler/res/layout/print_job_config_activity.xml @@ -5,7 +5,7 @@ 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 + 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, @@ -14,197 +14,264 @@ limitations under the License. --> - + android:orientation="vertical" + android:scrollbars="vertical"> - + android:orientation="vertical" + android:columnCount="2"> - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 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"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/packages/PrintSpooler/res/values/constants.xml b/packages/PrintSpooler/res/values/constants.xml index 7d2cdc37851f..96cdeb191dfa 100644 --- a/packages/PrintSpooler/res/values/constants.xml +++ b/packages/PrintSpooler/res/values/constants.xml @@ -16,8 +16,8 @@ - 1 - 2 + 0 + 1 @integer/page_option_value_all diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml index de6fb605d4ec..27540d7d6838 100644 --- a/packages/PrintSpooler/res/values/strings.xml +++ b/packages/PrintSpooler/res/values/strings.xml @@ -43,8 +43,14 @@ e.g. 1–5, 8 - - Invalid input + + Print preview + + + Install PDF viewer for preview + + + Printing app crashed diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java index 19f545d06569..1e1cc24f263e 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java @@ -23,6 +23,7 @@ import android.content.pm.PackageInfo; 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; @@ -50,6 +51,7 @@ import android.text.TextUtils; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; +import android.view.Choreographer; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; @@ -95,12 +97,18 @@ public class PrintJobConfigActivity extends Activity { private final PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this); + private Handler mHandler; + + private Editor mEditor; + private IPrinterDiscoveryObserver mPrinterDiscoveryObserver; private int mAppId; private int mPrintJobId; - private PrintAttributes mPrintAttributes; + 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; @@ -112,152 +120,6 @@ public class PrintJobConfigActivity extends Activity { private PrintDocumentInfo mPrintDocumentInfo; - // UI elements - - private EditText mCopiesEditText; - - private EditText mRangeEditText; - - private Spinner mDestinationSpinner; - public ArrayAdapter> mDestinationSpinnerAdapter; - - private Spinner mMediaSizeSpinner; - public ArrayAdapter> mMediaSizeSpinnerAdapter; - - private Spinner mColorModeSpinner; - public ArrayAdapter> mColorModeSpinnerAdapter; - - private Spinner mOrientationSpinner; - public ArrayAdapter> mOrientationSpinnerAdapter; - - private Spinner mRangeOptionsSpinner; - public ArrayAdapter> mRangeOptionsSpinnerAdapter; - - private Button mPrintButton; - - // TODO: Implement store/restore state. - - private final OnItemSelectedListener mOnItemSelectedListener = - new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView spinner, View view, int position, long id) { - if (spinner == mDestinationSpinner) { - updateUi(); - notifyPrintableStartIfNeeded(); - } else if (spinner == mMediaSizeSpinner) { - SpinnerItem mediaItem = mMediaSizeSpinnerAdapter.getItem(position); - mPrintAttributes.setMediaSize(mediaItem.value); - updatePrintableContentIfNeeded(); - } else if (spinner == mColorModeSpinner) { - SpinnerItem colorModeItem = - mColorModeSpinnerAdapter.getItem(position); - mPrintAttributes.setColorMode(colorModeItem.value); - } else if (spinner == mOrientationSpinner) { - SpinnerItem orientationItem = - mOrientationSpinnerAdapter.getItem(position); - mPrintAttributes.setOrientation(orientationItem.value); - } else if (spinner == mRangeOptionsSpinner) { - SpinnerItem rangeOptionItem = - mRangeOptionsSpinnerAdapter.getItem(position); - if (rangeOptionItem.value == getResources().getInteger( - R.integer.page_option_value_all)) { - mRangeEditText.setVisibility(View.INVISIBLE); - mRangeEditText.setEnabled(false); - mRangeEditText.setText(null); - mRangeEditText.setError(null); - mPrintButton.setEnabled(true); - } else if (rangeOptionItem.value == getResources().getInteger( - R.integer.page_option_value_page_range)) { - mRangeEditText.setVisibility(View.VISIBLE); - mRangeEditText.setEnabled(true); - mRangeEditText.requestFocus(); - mRangeEditText.setError(getString(R.string.invalid_input)); - InputMethodManager imm = (InputMethodManager) - getSystemService(INPUT_METHOD_SERVICE); - imm.showSoftInput(mRangeEditText, 0); - } - } - } - - @Override - public void onNothingSelected(AdapterView parent) { - /* do nothing*/ - } - }; - - private final TextWatcher mCopiesTextWatcher = new TextWatcher() { - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - /* do nothing */ - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - /* do nothing */ - } - - @Override - public void afterTextChanged(Editable editable) { - if (editable.length() == 0) { - mCopiesEditText.setError(getString(R.string.invalid_input)); - mPrintButton.setEnabled(false); - return; - } - final int copies = Integer.parseInt(editable.toString()); - if (copies < MIN_COPIES) { - mCopiesEditText.setError(getString(R.string.invalid_input)); - mPrintButton.setEnabled(false); - return; - } - mPrintAttributes.setCopies(copies); - mPrintButton.setEnabled(true); - } - }; - - private final TextWatcher mRangeTextWatcher = new TextWatcher() { - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - /* do nothing */ - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - /* do nothing */ - } - - @Override - public void afterTextChanged(Editable editable) { - String text = editable.toString(); - - if (TextUtils.isEmpty(text)) { - mRangeEditText.setError(getString(R.string.invalid_input)); - mPrintButton.setEnabled(false); - return; - } - - String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////"); - if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) { - mRangeEditText.setError(getString(R.string.invalid_input)); - mPrintButton.setEnabled(false); - return; - } - - Matcher matcher = PATTERN_DIGITS.matcher(text); - while (matcher.find()) { - String numericString = text.substring(matcher.start(), matcher.end()); - final int pageIndex = Integer.parseInt(numericString); - if (pageIndex < 1 || pageIndex > mPrintDocumentInfo.getPageCount()) { - mRangeEditText.setError(getString(R.string.invalid_input)); - mPrintButton.setEnabled(false); - return; - } - } - - mRangeEditText.setError(null); - mPrintButton.setEnabled(true); - } - }; - private final DeathRecipient mDeathRecipient = new DeathRecipient() { @Override public void binderDied() { @@ -266,207 +128,21 @@ public class PrintJobConfigActivity extends Activity { }; @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); - - 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); - } - - mPrintAttributes = getIntent().getParcelableExtra(EXTRA_ATTRIBUTES); - if (mPrintAttributes == null) { - mPrintAttributes = new PrintAttributes.Builder().create(); - } - - 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(); - } - - mPrinterDiscoveryObserver = new PrintDiscoveryObserver(getMainLooper()); - - bindUi(); - } - - @Override protected void onDestroy() { mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0); super.onDestroy(); } - private void bindUi() { - // Copies - mCopiesEditText = (EditText) findViewById(R.id.copies_edittext); - mCopiesEditText.setText(String.valueOf(MIN_COPIES)); - mCopiesEditText.addTextChangedListener(mCopiesTextWatcher); - - // Destination. - mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner); - mDestinationSpinnerAdapter = new ArrayAdapter>(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; - } - }; - mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter); - mDestinationSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - - // Media size. - mMediaSizeSpinner = (Spinner) findViewById(R.id.paper_size_spinner); - mMediaSizeSpinnerAdapter = new ArrayAdapter>(this, - R.layout.spinner_dropdown_item, R.id.title); - mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter); - mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - - // Color mode. - mColorModeSpinner = (Spinner) findViewById(R.id.color_spinner); - mColorModeSpinnerAdapter = new ArrayAdapter>(this, - R.layout.spinner_dropdown_item, R.id.title); - mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter); - mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - - // Orientation - mOrientationSpinner = (Spinner) findViewById(R.id.orientation_spinner); - mOrientationSpinnerAdapter = new ArrayAdapter>(this, - R.layout.spinner_dropdown_item, R.id.title); - mOrientationSpinner.setAdapter(mOrientationSpinnerAdapter); - mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener); - - // Range - mRangeEditText = (EditText) findViewById(R.id.page_range_edittext); - mRangeEditText.addTextChangedListener(mRangeTextWatcher); - - // Range options - mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner); - mRangeOptionsSpinnerAdapter = new ArrayAdapter>(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( - R.array.page_options_labels); - final int rangeOptionsCount = rangeOptionsLabels.length; - for (int i = 0; i < rangeOptionsCount; i++) { - mRangeOptionsSpinnerAdapter.add(new SpinnerItem( - rangeOptionsValues[i], rangeOptionsLabels[i])); - } - mRangeOptionsSpinner.setSelection(0); + @Override + protected void onCreate(Bundle bundle) { + super.onCreate(bundle); + setContentView(R.layout.print_job_config_activity); - mPrintButton = (Button) findViewById(R.id.print_button); - mPrintButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - mPrintConfirmed = true; - finish(); - } - }); - } + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN + | WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); - private void updateUi() { - final int selectedIndex = mDestinationSpinner.getSelectedItemPosition(); - PrinterInfo printer = mDestinationSpinnerAdapter.getItem(selectedIndex).value; - printer.getDefaults(mPrintAttributes); - - // Copies. - mCopiesEditText.setText(String.valueOf( - Math.max(mPrintAttributes.getCopies(), MIN_COPIES))); - mCopiesEditText.selectAll(); - - // Media size. - mMediaSizeSpinnerAdapter.clear(); - List mediaSizes = printer.getMediaSizes(); - final int mediaSizeCount = mediaSizes.size(); - for (int i = 0; i < mediaSizeCount; i++) { - MediaSize mediaSize = mediaSizes.get(i); - mMediaSizeSpinnerAdapter.add(new SpinnerItem( - mediaSize, mediaSize.getLabel())); - } - final int selectedMediaSizeIndex = mediaSizes.indexOf( - mPrintAttributes.getMediaSize()); - mMediaSizeSpinner.setOnItemSelectedListener(null); - mMediaSizeSpinner.setSelection(selectedMediaSizeIndex); - - // Color mode. - final int colorModes = printer.getColorModes(); - mColorModeSpinnerAdapter.clear(); - String[] colorModeLabels = getResources().getStringArray( - R.array.color_mode_labels); - int remainingColorModes = colorModes; - while (remainingColorModes != 0) { - final int colorBitOffset = Integer.numberOfTrailingZeros(remainingColorModes); - final int colorMode = 1 << colorBitOffset; - remainingColorModes &= ~colorMode; - mColorModeSpinnerAdapter.add(new SpinnerItem(colorMode, - colorModeLabels[colorBitOffset])); - } - final int selectedColorModeIndex = Integer.numberOfTrailingZeros( - (colorModes & mPrintAttributes.getColorMode())); - mColorModeSpinner.setSelection(selectedColorModeIndex); - - // Orientation. - final int orientations = printer.getOrientations(); - mOrientationSpinnerAdapter.clear(); - String[] orientationLabels = getResources().getStringArray( - R.array.orientation_labels); - int remainingOrientations = orientations; - while (remainingOrientations != 0) { - final int orientationBitOffset = Integer.numberOfTrailingZeros(remainingOrientations); - final int orientation = 1 << orientationBitOffset; - remainingOrientations &= ~orientation; - mOrientationSpinnerAdapter.add(new SpinnerItem(orientation, - orientationLabels[orientationBitOffset])); - } - final int selectedOrientationIndex = Integer.numberOfTrailingZeros( - (orientations & mPrintAttributes.getOrientation())); - mOrientationSpinner.setSelection(selectedOrientationIndex); + mHandler = new MyHandler(Looper.getMainLooper()); + mEditor = new Editor(); } @Override @@ -484,13 +160,12 @@ public class PrintJobConfigActivity extends Activity { } private void notifyPrintableStartIfNeeded() { - if (mDestinationSpinner.getSelectedItemPosition() < 0 + if (mEditor.getCurrentPrinter() == null || mStarted) { return; } mStarted = true; mRemotePrintAdapter.start(); - updatePrintableContentIfNeeded(); } private void updatePrintableContentIfNeeded() { @@ -498,43 +173,66 @@ public class PrintJobConfigActivity extends Activity { return; } - // TODO: Implement old attributes tracking - mPrintSpooler.setPrintJobAttributes(mPrintJobId, mPrintAttributes); + 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(new PrintAttributes.Builder().create(), - mPrintAttributes, new LayoutResultCallback() { + mRemotePrintAdapter.layout(mOldPrintAttributes, + mCurrPrintAttributes, new LayoutResultCallback() { @Override public void onLayoutFinished(PrintDocumentInfo info, boolean changed) { - mPrintDocumentInfo = info; + mHandler.obtainMessage(MyHandler.MSG_ON_LAYOUT_FINISHED, changed ? 1 : 0, + 0, info).sendToTarget(); + } - // TODO: Handle the case of unchanged content - mPrintSpooler.setPrintJobPrintDocumentInfo(mPrintJobId, info); + @Override + public void onLayoutFailed(CharSequence error) { + mHandler.obtainMessage(MyHandler.MSG_ON_LAYOUT_FAILED, error).sendToTarget(); + } + }, new Bundle()); + } - // TODO: Implement page selector. - final List pages = new ArrayList(); - pages.add(PageRange.ALL_PAGES); + private void handleOnLayoutFinished(PrintDocumentInfo info, boolean changed) { + mPrintDocumentInfo = info; - mRemotePrintAdapter.write(pages, new WriteResultCallback() { - @Override - public void onWriteFinished(List pages) { - updatePrintPreview(mRemotePrintAdapter.getFile()); - } + mEditor.updateUiIfNeeded(); - @Override - public void onWriteFailed(CharSequence error) { - Log.e(LOG_TAG, "Error write layout: " + error); - finishActivity(Activity.RESULT_CANCELED); - } - }); + // TODO: Handle the case of unchanged content + mPrintSpooler.setPrintJobPrintDocumentInfo(mPrintJobId, info); + + // TODO: Implement page selector. + final List pages = new ArrayList(); + pages.add(PageRange.ALL_PAGES); + + mRemotePrintAdapter.write(pages, new WriteResultCallback() { + @Override + public void onWriteFinished(List pages) { + mHandler.obtainMessage(MyHandler.MSG_ON_WRITE_FINISHED, pages).sendToTarget(); } @Override - public void onLayoutFailed(CharSequence error) { - Log.e(LOG_TAG, "Error during layout: " + error); - finishActivity(Activity.RESULT_CANCELED); + public void onWriteFailed(CharSequence error) { + mHandler.obtainMessage(MyHandler.MSG_ON_WRITE_FAILED, error).sendToTarget(); } - }, new Bundle()); + }); + } + + private void handleOnLayoutFailed(CharSequence error) { + Log.e(LOG_TAG, "Error during layout: " + error); + finishActivity(Activity.RESULT_CANCELED); + } + + private void handleOnWriteFinished(List pages) { + // TODO: Now we have to allow the preview button + mEditor.updatePrintPreview(mRemotePrintAdapter.getFile()); + } + + private void handleOnWriteFailed(CharSequence error) { + Log.e(LOG_TAG, "Error write layout: " + error); + finishActivity(Activity.RESULT_CANCELED); } private void notifyPrintableFinishIfNeeded() { @@ -542,11 +240,14 @@ public class PrintJobConfigActivity extends Activity { return; } - mRemotePrintAdapter.finish(!mPrintConfirmed); + if (!mPrintConfirmed) { + mRemotePrintAdapter.cancel(); + } + mRemotePrintAdapter.finish(); + PrinterInfo printer = mEditor.getCurrentPrinter(); // If canceled or no printer, nothing to do. - final int selectedIndex = mDestinationSpinner.getSelectedItemPosition(); - if (!mPrintConfirmed || selectedIndex < 0) { + if (!mPrintConfirmed || printer == null) { // Update the print job's status. mPrintSpooler.setPrintJobState(mPrintJobId, PrintJobInfo.STATE_CANCELED); @@ -554,10 +255,7 @@ public class PrintJobConfigActivity extends Activity { } // Update the print job's printer. - SpinnerItem printerItem = - mDestinationSpinnerAdapter.getItem(selectedIndex); - PrinterId printerId = printerItem.value.getId(); - mPrintSpooler.setPrintJobPrinterId(mPrintJobId, printerId); + mPrintSpooler.setPrintJobPrinterId(mPrintJobId, printer.getId()); // Update the print job's status. mPrintSpooler.setPrintJobState(mPrintJobId, @@ -574,50 +272,11 @@ public class PrintJobConfigActivity extends Activity { } } - private void updatePrintPreview(File file) { - // TODO: Implement - } - - private void addPrinters(List addedPrinters) { - final int addedPrinterCount = addedPrinters.size(); - for (int i = 0; i < addedPrinterCount; i++) { - PrinterInfo addedPrinter = addedPrinters.get(i); - boolean duplicate = false; - final int existingPrinterCount = mDestinationSpinnerAdapter.getCount(); - for (int j = 0; j < existingPrinterCount; j++) { - PrinterInfo existingPrinter = mDestinationSpinnerAdapter.getItem(j).value; - if (addedPrinter.getId().equals(existingPrinter.getId())) { - duplicate = true; - break; - } - } - if (!duplicate) { - mDestinationSpinnerAdapter.add(new SpinnerItem( - addedPrinter, addedPrinter.getLabel())); - } else { - Log.w(LOG_TAG, "Skipping a duplicate printer: " + addedPrinter); - } - } - } - - private void removePrinters(List pritnerIds) { - final int printerIdCount = pritnerIds.size(); - for (int i = 0; i < printerIdCount; i++) { - PrinterId removedPrinterId = pritnerIds.get(i); - boolean removed = false; - final int existingPrinterCount = mDestinationSpinnerAdapter.getCount(); - for (int j = 0; j < existingPrinterCount; j++) { - PrinterInfo existingPrinter = mDestinationSpinnerAdapter.getItem(j).value; - if (removedPrinterId.equals(existingPrinter.getId())) { - mDestinationSpinnerAdapter.remove(mDestinationSpinnerAdapter.getItem(j)); - removed = true; - break; - } - } - if (!removed) { - Log.w(LOG_TAG, "Ignoring not added printer with id: " + removedPrinterId); - } - } + private boolean hasPdfViewer() { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setType("application/pdf"); + return !getPackageManager().queryIntentActivities(intent, + PackageManager.MATCH_DEFAULT_ONLY).isEmpty(); } // Caution: Use this only for debugging @@ -659,16 +318,11 @@ public class PrintJobConfigActivity extends Activity { switch (message.what) { case MESSAGE_ADD_DICOVERED_PRINTERS: { List printers = (List) message.obj; - addPrinters(printers); - // Just added the first printer, so select it and start printing. - if (mDestinationSpinnerAdapter.getCount() == 1) { - mDestinationSpinner.setSelection(0); - } + mEditor.addPrinters(printers); } break; case MESSAGE_REMOVE_DICOVERED_PRINTERS: { List printerIds = (List) message.obj; - removePrinters(printerIds); - // TODO: Handle removing the last printer. + mEditor.removePrinters(printerIds); } break; } } @@ -725,6 +379,11 @@ public class PrintJobConfigActivity extends Activity { return true; } + @Override + public void setError(CharSequence error, Drawable icon) { + setCompoundDrawables(null, null, icon, null); + } + protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { if (!gainFocus) { @@ -733,4 +392,611 @@ public class PrintJobConfigActivity extends Activity { 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; + + public MyHandler(Looper looper) { + super(looper, null, false); + } + + @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 pages = (List) message.obj; + handleOnWriteFinished(pages); + } break; + + case MSG_ON_WRITE_FAILED: { + CharSequence error = (CharSequence) message.obj; + handleOnWriteFailed(error); + } break; + } + } + } + + private class Editor { + private EditText mCopiesEditText; + + private EditText mRangeEditText; + + private Spinner mDestinationSpinner; + public ArrayAdapter> mDestinationSpinnerAdapter; + + private Spinner mMediaSizeSpinner; + public ArrayAdapter> mMediaSizeSpinnerAdapter; + + private Spinner mColorModeSpinner; + public ArrayAdapter> mColorModeSpinnerAdapter; + + private Spinner mOrientationSpinner; + public ArrayAdapter> mOrientationSpinnerAdapter; + + private Spinner mRangeOptionsSpinner; + public ArrayAdapter> mRangeOptionsSpinnerAdapter; + + private Button mPrintPreviewButton; + + private 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); + } + updateUiIfNeeded(); + notifyPrintableStartIfNeeded(); + updatePrintableContentIfNeeded(); + } else if (spinner == mMediaSizeSpinner) { + SpinnerItem mediaItem = mMediaSizeSpinnerAdapter.getItem(position); + mOldPrintAttributes.copyFrom(mCurrPrintAttributes); + mCurrPrintAttributes.setMediaSize(mediaItem.value); + updatePrintableContentIfNeeded(); + } else if (spinner == mColorModeSpinner) { + SpinnerItem colorModeItem = + mColorModeSpinnerAdapter.getItem(position); + mOldPrintAttributes.copyFrom(mCurrPrintAttributes); + mCurrPrintAttributes.setColorMode(colorModeItem.value); + updatePrintableContentIfNeeded(); + } else if (spinner == mOrientationSpinner) { + SpinnerItem orientationItem = + mOrientationSpinnerAdapter.getItem(position); + mOldPrintAttributes.copyFrom(mCurrPrintAttributes); + mCurrPrintAttributes.setOrientation(orientationItem.value); + updatePrintableContentIfNeeded(); + } else if (spinner == mRangeOptionsSpinner) { + updateUiIfNeeded(); + updatePrintableContentIfNeeded(); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + /* do nothing*/ + } + }; + + private final TextWatcher mCopiesTextWatcher = new TextWatcher() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + /* do nothing */ + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + /* do nothing */ + } + + @Override + public void afterTextChanged(Editable editable) { + if (editable.length() == 0) { + mCopiesEditText.setError(""); + mPrintButton.setEnabled(false); + return; + } + final int copies = Integer.parseInt(editable.toString()); + if (copies < MIN_COPIES) { + mCopiesEditText.setError(""); + mPrintButton.setEnabled(false); + return; + } + mOldPrintAttributes.copyFrom(mCurrPrintAttributes); + mCurrPrintAttributes.setCopies(copies); + } + }; + + private final TextWatcher mRangeTextWatcher = new TextWatcher() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + /* do nothing */ + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + /* do nothing */ + } + + @Override + public void afterTextChanged(Editable editable) { + String text = editable.toString(); + + if (TextUtils.isEmpty(text)) { + mRangeEditText.setError(""); + mPrintButton.setEnabled(false); + return; + } + + String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////"); + if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) { + mRangeEditText.setError(""); + mPrintButton.setEnabled(false); + return; + } + + Matcher matcher = PATTERN_DIGITS.matcher(text); + while (matcher.find()) { + String numericString = text.substring(matcher.start(), matcher.end()); + final int pageIndex = Integer.parseInt(numericString); + if (pageIndex < 1 || pageIndex > mPrintDocumentInfo.getPageCount()) { + mRangeEditText.setError(""); + mPrintButton.setEnabled(false); + return; + } + } + + 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); + } + + 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(); + } + + mPrinterDiscoveryObserver = new PrintDiscoveryObserver(getMainLooper()); + + bindUi(); + } + + private void bindUi() { + // 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>( + 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; + } + }; + mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter); + mDestinationSpinner.setOnItemSelectedListener(mOnItemSelectedListener); + + // Media size. + mMediaSizeSpinner = (Spinner) findViewById(R.id.paper_size_spinner); + mMediaSizeSpinnerAdapter = new ArrayAdapter>( + PrintJobConfigActivity.this, + R.layout.spinner_dropdown_item, R.id.title); + mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter); + mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); + + // Color mode. + mColorModeSpinner = (Spinner) findViewById(R.id.color_spinner); + mColorModeSpinnerAdapter = new ArrayAdapter>( + PrintJobConfigActivity.this, + R.layout.spinner_dropdown_item, R.id.title); + mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter); + mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); + + // Orientation + mOrientationSpinner = (Spinner) findViewById(R.id.orientation_spinner); + mOrientationSpinnerAdapter = new ArrayAdapter>( + PrintJobConfigActivity.this, + R.layout.spinner_dropdown_item, R.id.title); + mOrientationSpinner.setAdapter(mOrientationSpinnerAdapter); + mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener); + + // Range + mRangeEditText = (EditText) findViewById(R.id.page_range_edittext); + mRangeEditText.addTextChangedListener(mRangeTextWatcher); + + // Range options + mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner); + mRangeOptionsSpinnerAdapter = new ArrayAdapter>( + 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( + R.array.page_options_labels); + final int rangeOptionsCount = rangeOptionsLabels.length; + for (int i = 0; i < rangeOptionsCount; i++) { + mRangeOptionsSpinnerAdapter.add(new SpinnerItem( + rangeOptionsValues[i], rangeOptionsLabels[i])); + } + mRangeOptionsSpinner.setSelection(0); + + mPrintPreviewButton = (Button) findViewById(R.id.print_preview_button); + mPrintPreviewButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + // TODO: Implement + } + }); + + mPrintButton = (Button) findViewById(R.id.print_button); + mPrintButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mPrintConfirmed = true; + finish(); + } + }); + } + + private void updateUiIfNeeded() { + final int selectedIndex = mDestinationSpinner.getSelectedItemPosition(); + + if (selectedIndex < 0) { + // Destination + mDestinationSpinner.setEnabled(false); + + // Copies + mCopiesEditText.setText("1"); + mCopiesEditText.setEnabled(false); + + // Media size + mMediaSizeSpinner.setOnItemSelectedListener(null); + mMediaSizeSpinner.setSelection(AdapterView.INVALID_POSITION); + mMediaSizeSpinner.setEnabled(false); + + // Color mode + mColorModeSpinner.setOnItemSelectedListener(null); + mColorModeSpinner.setSelection(AdapterView.INVALID_POSITION); + mColorModeSpinner.setEnabled(false); + + // Orientation + mOrientationSpinner.setOnItemSelectedListener(null); + mOrientationSpinner.setSelection(AdapterView.INVALID_POSITION); + mOrientationSpinner.setEnabled(false); + + // Range + mRangeOptionsSpinner.setOnItemSelectedListener(null); + mRangeOptionsSpinner.setSelection(0); + mRangeOptionsSpinner.setEnabled(false); + mRangeEditText.setText(""); + mRangeEditText.setEnabled(false); + mRangeEditText.setVisibility(View.INVISIBLE); + + // Print preview + mPrintPreviewButton.setEnabled(false); + mPrintPreviewButton.setText(getString(R.string.print_preview)); + + // Print + mPrintButton.setEnabled(false); + } else { + PrintAttributes defaultAttributes = mTempPrintAttributes; + PrinterInfo printer = mDestinationSpinnerAdapter.getItem(selectedIndex).value; + printer.getDefaults(defaultAttributes); + + // Destination + mDestinationSpinner.setEnabled(true); + + // Copies + mCopiesEditText.setEnabled(true); + + // Media size. + List mediaSizes = printer.getMediaSizes(); + boolean mediaSizesChanged = false; + final int mediaSizeCount = mediaSizes.size(); + if (mediaSizeCount != mMediaSizeSpinnerAdapter.getCount()) { + mediaSizesChanged = true; + } else { + for (int i = 0; i < mediaSizeCount; i++) { + if (!mediaSizes.get(i).equals(mMediaSizeSpinnerAdapter.getItem(i).value)) { + mediaSizesChanged = true; + break; + } + } + } + if (mediaSizesChanged) { + mMediaSizeSpinnerAdapter.clear(); + for (int i = 0; i < mediaSizeCount; i++) { + MediaSize mediaSize = mediaSizes.get(i); + mMediaSizeSpinnerAdapter.add(new SpinnerItem( + mediaSize, mediaSize.getLabel())); + } + if (mediaSizeCount > 0) { + mMediaSizeSpinner.setEnabled(true); + final int selectedMediaSizeIndex = Math.max(mediaSizes.indexOf( + defaultAttributes.getMediaSize()), 0); + mMediaSizeSpinner.setOnItemSelectedListener(null); + mMediaSizeSpinner.setSelection(selectedMediaSizeIndex); + } + } + + // Color mode. + final int colorModes = printer.getColorModes(); + boolean colorModesChanged = false; + if (Integer.bitCount(colorModes) != mColorModeSpinnerAdapter.getCount()) { + colorModesChanged = true; + } else { + int remainingColorModes = colorModes; + while (remainingColorModes != 0) { + final int colorBitOffset = Integer.numberOfTrailingZeros( + remainingColorModes); + final int colorMode = 1 << colorBitOffset; + remainingColorModes &= ~colorMode; + if (colorMode != mColorModeSpinnerAdapter.getItem(colorBitOffset).value) { + colorModesChanged = true; + break; + } + } + } + if (colorModesChanged) { + mColorModeSpinnerAdapter.clear(); + String[] colorModeLabels = getResources().getStringArray( + R.array.color_mode_labels); + int remainingColorModes = colorModes; + while (remainingColorModes != 0) { + final int colorBitOffset = Integer.numberOfTrailingZeros( + remainingColorModes); + final int colorMode = 1 << colorBitOffset; + remainingColorModes &= ~colorMode; + mColorModeSpinnerAdapter.add(new SpinnerItem(colorMode, + colorModeLabels[colorBitOffset])); + } + if (colorModes > 0) { + mColorModeSpinner.setEnabled(true); + final int selectedColorModeIndex = Integer.numberOfTrailingZeros( + (colorModes & defaultAttributes.getColorMode())); + mColorModeSpinner.setOnItemSelectedListener(null); + mColorModeSpinner.setSelection(selectedColorModeIndex); + } + } + + // Orientation. + final int orientations = printer.getOrientations(); + boolean orientationsChanged = false; + if (Integer.bitCount(orientations) != mOrientationSpinnerAdapter.getCount()) { + orientationsChanged = true; + } else { + int remainingOrientations = orientations; + while (remainingOrientations != 0) { + final int orientationBitOffset = Integer.numberOfTrailingZeros( + remainingOrientations); + final int orientation = 1 << orientationBitOffset; + remainingOrientations &= ~orientation; + if (orientation != mOrientationSpinnerAdapter.getItem( + orientationBitOffset).value) { + orientationsChanged = true; + break; + } + } + } + if (orientationsChanged) { + mOrientationSpinnerAdapter.clear(); + String[] orientationLabels = getResources().getStringArray( + R.array.orientation_labels); + int remainingOrientations = orientations; + while (remainingOrientations != 0) { + final int orientationBitOffset = Integer.numberOfTrailingZeros( + remainingOrientations); + final int orientation = 1 << orientationBitOffset; + remainingOrientations &= ~orientation; + mOrientationSpinnerAdapter.add(new SpinnerItem(orientation, + orientationLabels[orientationBitOffset])); + } + if (orientations > 0) { + mOrientationSpinner.setEnabled(true); + final int selectedOrientationIndex = Integer.numberOfTrailingZeros( + (orientations & defaultAttributes.getOrientation())); + mOrientationSpinner.setOnItemSelectedListener(null); + mOrientationSpinner.setSelection(selectedOrientationIndex); + } + } + + // Range options + if (mPrintDocumentInfo != null && (mPrintDocumentInfo.getPageCount() > 1 + || mPrintDocumentInfo.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); + } + } else { + mRangeOptionsSpinner.setOnItemSelectedListener(null); + mRangeOptionsSpinner.setSelection(0); + mRangeOptionsSpinner.setEnabled(false); + mRangeEditText.setEnabled(false); + mRangeEditText.setText(""); + mRangeEditText.setVisibility(View.INVISIBLE); + } + + // Print preview + mPrintPreviewButton.setEnabled(true); + if (hasPdfViewer()) { + mPrintPreviewButton.setText(getString(R.string.print_preview)); + } else { + mPrintPreviewButton.setText(getString(R.string.install_for_print_preview)); + } + + // Print + mPrintButton.setEnabled(true); + } + + // 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, + new Runnable() { + @Override + public void run() { + mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); + mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener); + 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; + } + + public void addPrinters(List addedPrinters) { + final int addedPrinterCount = addedPrinters.size(); + for (int i = 0; i < addedPrinterCount; i++) { + PrinterInfo addedPrinter = addedPrinters.get(i); + boolean duplicate = false; + final int existingPrinterCount = mDestinationSpinnerAdapter.getCount(); + for (int j = 0; j < existingPrinterCount; j++) { + PrinterInfo existingPrinter = mDestinationSpinnerAdapter.getItem(j).value; + if (addedPrinter.getId().equals(existingPrinter.getId())) { + duplicate = true; + break; + } + } + if (!duplicate) { + mDestinationSpinnerAdapter.add(new SpinnerItem( + addedPrinter, addedPrinter.getLabel())); + } else { + Log.w(LOG_TAG, "Skipping a duplicate printer: " + addedPrinter); + } + } + + if (mDestinationSpinner.getSelectedItemPosition() == AdapterView.INVALID_POSITION + && mDestinationSpinnerAdapter.getCount() > 0) { + mDestinationSpinner.setSelection(0); + } + } + + public void removePrinters(List pritnerIds) { + final int printerIdCount = pritnerIds.size(); + for (int i = 0; i < printerIdCount; i++) { + PrinterId removedPrinterId = pritnerIds.get(i); + boolean removed = false; + final int existingPrinterCount = mDestinationSpinnerAdapter.getCount(); + for (int j = 0; j < existingPrinterCount; j++) { + PrinterInfo existingPrinter = mDestinationSpinnerAdapter.getItem(j).value; + if (removedPrinterId.equals(existingPrinter.getId())) { + mDestinationSpinnerAdapter.remove(mDestinationSpinnerAdapter.getItem(j)); + removed = true; + break; + } + } + if (!removed) { + Log.w(LOG_TAG, "Ignoring not added printer with id: " + removedPrinterId); + } + } + + if (mDestinationSpinner.getSelectedItemPosition() != AdapterView.INVALID_POSITION + && mDestinationSpinnerAdapter.getCount() == 0) { + mDestinationSpinner.setSelection(AdapterView.INVALID_POSITION); + } + } + + private void updatePrintPreview(File file) { + // TODO: Implement + } + } } diff --git a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java index 2bf62eebe8bc..25bb37c51710 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java +++ b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java @@ -32,6 +32,8 @@ import android.print.PrintDocumentInfo; import android.util.Log; import android.util.Slog; +import libcore.io.IoUtils; + import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -41,8 +43,6 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.List; -import libcore.io.IoUtils; - /** * This class represents a remote print document adapter instance. */ @@ -53,10 +53,12 @@ final class RemotePrintDocumentAdapter { public static final int STATE_INITIALIZED = 0; public static final int STATE_START_COMPLETED = 1; - public static final int STATE_LAYOUT_COMPLETED = 2; - public static final int STATE_WRITE_COMPLETED = 3; - public static final int STATE_FINISH_COMPLETED = 4; - public static final int STATE_FAILED = 6; + public static final int STATE_LAYOUT_STARTED = 2; + public static final int STATE_LAYOUT_COMPLETED = 3; + public static final int STATE_WRITE_STARTED = 4; + public static final int STATE_WRITE_COMPLETED = 5; + public static final int STATE_FINISH_COMPLETED = 6; + public static final int STATE_FAILED = 7; private final Object mLock = new Object(); @@ -94,7 +96,6 @@ final class RemotePrintDocumentAdapter { Log.i(LOG_TAG, "start()"); } synchronized (mLock) { - mTaskQueue.add(this); if (mState != STATE_INITIALIZED) { throw new IllegalStateException("Invalid state: " + mState); } @@ -109,7 +110,15 @@ final class RemotePrintDocumentAdapter { } return null; } + + @Override + public void cancel() { + /* cannot be cancelled */ + } }; + synchronized (mLock) { + mTaskQueue.add(task); + } task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null); } @@ -117,29 +126,37 @@ final class RemotePrintDocumentAdapter { LayoutResultCallback callback, Bundle metadata) { LayoutAsyncTask task = new LayoutAsyncTask(oldAttributes, newAttributes, callback, metadata); + synchronized (mLock) { + mTaskQueue.add(task); + } task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null); } public void write(List pages, WriteResultCallback callback) { WriteAsyncTask task = new WriteAsyncTask(pages, callback); + synchronized (mLock) { + mTaskQueue.add(task); + } task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null); } - public void finish(final boolean abortPendingWork) { + public void cancel() { + synchronized (mLock) { + final int taskCount = mTaskQueue.size(); + for (int i = taskCount - 1; i >= 0; i--) { + mTaskQueue.remove(i).cancel(); + } + } + } + + public void finish() { QueuedAsyncTask task = new QueuedAsyncTask() { @Override protected Void doInBackground(Void... params) { if (DEBUG) { - Log.i(LOG_TAG, "finish"); + Log.i(LOG_TAG, "finish()"); } synchronized (mLock) { - if (abortPendingWork) { - final int taskCount = mTaskQueue.size(); - for (int i = taskCount - 1; i >= 0; i--) { - mTaskQueue.remove(i).cancel(); - } - } - mTaskQueue.add(this); if (mState < STATE_START_COMPLETED) { return null; } @@ -155,7 +172,15 @@ final class RemotePrintDocumentAdapter { } return null; } + + @Override + public void cancel() { + /* cannot be cancelled */ + } }; + synchronized (mLock) { + mTaskQueue.add(task); + } task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null); } @@ -179,6 +204,9 @@ final class RemotePrintDocumentAdapter { new ILayoutResultCallback.Stub() { @Override public void onLayoutStarted(ICancellationSignal cancellationSignal) { + if (DEBUG) { + Log.i(LOG_TAG, "onLayoutStarted()"); + } synchronized (mLock) { mCancellationSignal = cancellationSignal; if (isCancelled()) { @@ -189,30 +217,43 @@ final class RemotePrintDocumentAdapter { @Override public void onLayoutFinished(PrintDocumentInfo info, boolean changed) { + if (DEBUG) { + Log.i(LOG_TAG, "onLayoutFinished()"); + } + final boolean cancelled; synchronized (mLock) { + cancelled = isCancelled(); mCancellationSignal = null; - mCompleted = true; + mState = STATE_LAYOUT_COMPLETED; + mTaskQueue.remove(this); mLock.notifyAll(); } - mCallback.onLayoutFinished(info, changed); + if (!cancelled) { + mCallback.onLayoutFinished(info, changed); + } } @Override public void onLayoutFailed(CharSequence error) { + if (DEBUG) { + Log.i(LOG_TAG, "onLayoutFailed()"); + } + final boolean cancelled; synchronized (mLock) { + cancelled = isCancelled(); mCancellationSignal = null; - mCompleted = true; + mState = STATE_LAYOUT_COMPLETED; + mTaskQueue.remove(this); mLock.notifyAll(); } - Slog.e(LOG_TAG, "Error laying out print document: " + error); - mCallback.onLayoutFailed(error); + if (!cancelled) { + mCallback.onLayoutFailed(error); + } } }; private ICancellationSignal mCancellationSignal; - private boolean mCompleted; - public LayoutAsyncTask(PrintAttributes oldAttributes, PrintAttributes newAttributes, LayoutResultCallback callback, Bundle metadata) { mOldAttributes = oldAttributes; @@ -233,25 +274,22 @@ final class RemotePrintDocumentAdapter { @Override protected Void doInBackground(Void... params) { synchronized (mLock) { - mTaskQueue.add(this); + if (DEBUG) { + Log.i(LOG_TAG, "layout()"); + } if (mState != STATE_START_COMPLETED && mState != STATE_LAYOUT_COMPLETED && mState != STATE_WRITE_COMPLETED) { throw new IllegalStateException("Invalid state: " + mState); } + mState = STATE_LAYOUT_STARTED; } try { mRemoteInterface.layout(mOldAttributes, mNewAttributes, mILayoutResultCallback, mMetadata); synchronized (mLock) { while (true) { - if (isCancelled()) { - mTaskQueue.remove(this); - break; - } - if (mCompleted) { - mState = STATE_LAYOUT_COMPLETED; - mTaskQueue.remove(this); + if (mState == STATE_LAYOUT_COMPLETED) { break; } try { @@ -264,6 +302,8 @@ final class RemotePrintDocumentAdapter { } catch (RemoteException re) { Slog.e(LOG_TAG, "Error calling layout", re); mState = STATE_FAILED; + mTaskQueue.remove(this); + notifyLayoutFailedQuietly(); } return null; } @@ -274,10 +314,19 @@ final class RemotePrintDocumentAdapter { mCancellationSignal.cancel(); } catch (RemoteException re) { Slog.e(LOG_TAG, "Error cancelling layout", re); + notifyLayoutFailedQuietly(); } } } + public void notifyLayoutFailedQuietly() { + try { + mILayoutResultCallback.onLayoutFailed(null); + } catch (RemoteException re) { + /* ignore */ + } + } + private void throwIfCancelledLocked() { if (isCancelled()) { throw new IllegalStateException("Already cancelled"); @@ -295,6 +344,9 @@ final class RemotePrintDocumentAdapter { new IWriteResultCallback.Stub() { @Override public void onWriteStarted(ICancellationSignal cancellationSignal) { + if (DEBUG) { + Log.i(LOG_TAG, "onWriteStarted()"); + } synchronized (mLock) { mCancellationSignal = cancellationSignal; if (isCancelled()) { @@ -305,9 +357,13 @@ final class RemotePrintDocumentAdapter { @Override public void onWriteFinished(List pages) { + if (DEBUG) { + Log.i(LOG_TAG, "onWriteFinished()"); + } synchronized (mLock) { mCancellationSignal = null; - mCompleted = true; + mState = STATE_WRITE_COMPLETED; + mTaskQueue.remove(this); mLock.notifyAll(); } mCallback.onWriteFinished(pages); @@ -315,9 +371,13 @@ final class RemotePrintDocumentAdapter { @Override public void onWriteFailed(CharSequence error) { + if (DEBUG) { + Log.i(LOG_TAG, "onWriteFailed()"); + } synchronized (mLock) { mCancellationSignal = null; - mCompleted = true; + mState = STATE_WRITE_COMPLETED; + mTaskQueue.remove(this); mLock.notifyAll(); } Slog.e(LOG_TAG, "Error writing print document: " + error); @@ -327,8 +387,6 @@ final class RemotePrintDocumentAdapter { private ICancellationSignal mCancellationSignal; - private boolean mCompleted; - private Thread mWriteThread; public WriteAsyncTask(List pages, WriteResultCallback callback) { @@ -349,14 +407,14 @@ final class RemotePrintDocumentAdapter { @Override protected Void doInBackground(Void... params) { if (DEBUG) { - Log.i(LOG_TAG, "print()"); + Log.i(LOG_TAG, "write()"); } synchronized (mLock) { - mTaskQueue.add(this); if (mState != STATE_LAYOUT_COMPLETED && mState != STATE_WRITE_COMPLETED) { throw new IllegalStateException("Invalid state: " + mState); } + mState = STATE_WRITE_STARTED; } InputStream in = null; OutputStream out = null; @@ -394,13 +452,7 @@ final class RemotePrintDocumentAdapter { } synchronized (mLock) { while (true) { - if (isCancelled()) { - mTaskQueue.remove(this); - break; - } - if (mCompleted) { - mState = STATE_WRITE_COMPLETED; - mTaskQueue.remove(this); + if (mState == STATE_WRITE_COMPLETED) { break; } try { @@ -413,9 +465,13 @@ final class RemotePrintDocumentAdapter { } catch (RemoteException re) { Slog.e(LOG_TAG, "Error writing print document", re); mState = STATE_FAILED; + mTaskQueue.remove(this); + notifyWriteFailedQuietly(); } catch (IOException ioe) { Slog.e(LOG_TAG, "Error writing print document", ioe); mState = STATE_FAILED; + mTaskQueue.remove(this); + notifyWriteFailedQuietly(); } finally { IoUtils.closeQuietly(in); IoUtils.closeQuietly(out); @@ -431,10 +487,19 @@ final class RemotePrintDocumentAdapter { mCancellationSignal.cancel(); } catch (RemoteException re) { Slog.e(LOG_TAG, "Error cancelling layout", re); + notifyWriteFailedQuietly(); } } } + private void notifyWriteFailedQuietly() { + try { + mIWriteResultCallback.onWriteFailed(null); + } catch (RemoteException re) { + /* ignore */ + } + } + private void throwIfCancelledLocked() { if (isCancelled()) { throw new IllegalStateException("Already cancelled"); -- 2.11.0