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.java794
1 files changed, 794 insertions, 0 deletions
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
new file mode 100644
index 0000000..ae2fe5c
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -0,0 +1,794 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printspooler;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.IBinder.DeathRecipient;
+import android.print.IPrintAdapter;
+import android.print.IPrintManager;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintAttributes.Tray;
+import android.print.PrintJobInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.Spinner;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Activity for configuring a print job.
+ */
+public class PrintJobConfigActivity extends Activity {
+
+ private static final boolean DEBUG = false;
+
+ private static final String LOG_TAG = PrintJobConfigActivity.class.getSimpleName();
+
+ public static final String EXTRA_PRINTABLE = "printable";
+ public static final String EXTRA_APP_ID = "appId";
+ public static final String EXTRA_ATTRIBUTES = "attributes";
+ public static final String EXTRA_PRINT_JOB_ID = "printJobId";
+
+ private static final int MIN_COPIES = 1;
+
+ private final List<QueuedAsyncTask<?>> mTaskQueue = new ArrayList<QueuedAsyncTask<?>>();
+
+ private IPrintManager mPrintManager;
+
+ private IPrinterDiscoveryObserver mPrinterDiscoveryObserver;
+
+ private int mAppId;
+ private int mPrintJobId;
+
+ private PrintAttributes mPrintAttributes;
+
+ private final PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this);
+
+ private RemotePrintAdapter mRemotePrintAdapter;
+
+ // UI elements
+
+ private EditText mCopiesEditText;
+
+ private Spinner mDestinationSpinner;
+ public ArrayAdapter<SpinnerItem<PrinterInfo>> mDestinationSpinnerAdapter;
+
+ private Spinner mMediaSizeSpinner;
+ public ArrayAdapter<SpinnerItem<MediaSize>> mMediaSizeSpinnerAdapter;
+
+ private Spinner mResolutionSpinner;
+ public ArrayAdapter<SpinnerItem<Resolution>> mResolutionSpinnerAdapter;
+
+ private Spinner mInputTraySpinner;
+ public ArrayAdapter<SpinnerItem<Tray>> mInputTraySpinnerAdapter;
+
+ private Spinner mOutputTraySpinner;
+ public ArrayAdapter<SpinnerItem<Tray>> mOutputTraySpinnerAdapter;
+
+ private Spinner mDuplexModeSpinner;
+ public ArrayAdapter<SpinnerItem<Integer>> mDuplexModeSpinnerAdapter;
+
+ private Spinner mColorModeSpinner;
+ public ArrayAdapter<SpinnerItem<Integer>> mColorModeSpinnerAdapter;
+
+ private Spinner mFittingModeSpinner;
+ public ArrayAdapter<SpinnerItem<Integer>> mFittingModeSpinnerAdapter;
+
+ private Spinner mOrientationSpinner;
+ public ArrayAdapter<SpinnerItem<Integer>> mOrientationSpinnerAdapter;
+
+ private boolean mPrintStarted;
+
+ private boolean mPrintConfirmed;
+
+ private IBinder mPrinable;
+
+ // 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<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position);
+ mPrintAttributes.setMediaSize(mediaItem.value);
+ updatePrintableContentIfNeeded();
+ } else if (spinner == mResolutionSpinner) {
+ SpinnerItem<Resolution> resolutionItem =
+ mResolutionSpinnerAdapter.getItem(position);
+ mPrintAttributes.setResolution(resolutionItem.value);
+ updatePrintableContentIfNeeded();
+ } else if (spinner == mInputTraySpinner) {
+ SpinnerItem<Tray> inputTrayItem =
+ mInputTraySpinnerAdapter.getItem(position);
+ mPrintAttributes.setInputTray(inputTrayItem.value);
+ } else if (spinner == mOutputTraySpinner) {
+ SpinnerItem<Tray> outputTrayItem =
+ mOutputTraySpinnerAdapter.getItem(position);
+ mPrintAttributes.setOutputTray(outputTrayItem.value);
+ } else if (spinner == mDuplexModeSpinner) {
+ SpinnerItem<Integer> duplexModeItem =
+ mDuplexModeSpinnerAdapter.getItem(position);
+ mPrintAttributes.setDuplexMode(duplexModeItem.value);
+ } else if (spinner == mColorModeSpinner) {
+ SpinnerItem<Integer> colorModeItem =
+ mColorModeSpinnerAdapter.getItem(position);
+ mPrintAttributes.setColorMode(colorModeItem.value);
+ } else if (spinner == mFittingModeSpinner) {
+ SpinnerItem<Integer> fittingModeItem =
+ mFittingModeSpinnerAdapter.getItem(position);
+ mPrintAttributes.setFittingMode(fittingModeItem.value);
+ } else if (spinner == mOrientationSpinner) {
+ SpinnerItem<Integer> orientationItem =
+ mOrientationSpinnerAdapter.getItem(position);
+ mPrintAttributes.setOrientation(orientationItem.value);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ /* do nothing*/
+ }
+ };
+
+ private final TextWatcher mTextWatcher = new TextWatcher() {
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ final int copies = Integer.parseInt(mCopiesEditText.getText().toString());
+ mPrintAttributes.setCopies(copies);
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ /* do nothing */
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ /* do nothing */
+ }
+ };
+
+ private final InputFilter mInputFilter = new InputFilter() {
+ @Override
+ public CharSequence filter(CharSequence source, int start, int end,
+ Spanned dest, int dstart, int dend) {
+ StringBuffer text = new StringBuffer(dest.toString());
+ text.replace(dstart, dend, source.subSequence(start, end).toString());
+ if (TextUtils.isEmpty(text)) {
+ return dest;
+ }
+ final int copies = Integer.parseInt(text.toString());
+ if (copies < MIN_COPIES) {
+ return dest;
+ }
+ return null;
+ }
+ };
+
+ private final DeathRecipient mDeathRecipient = new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ finish();
+ }
+ };
+
+ @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);
+
+ mPrintManager = (IPrintManager) IPrintManager.Stub.asInterface(
+ ServiceManager.getService(PRINT_SERVICE));
+
+ 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();
+ }
+
+ mPrinable = extras.getBinder(EXTRA_PRINTABLE);
+ if (mPrinable == null) {
+ throw new IllegalArgumentException("Printable cannot be null");
+ }
+ mRemotePrintAdapter = new RemotePrintAdapter(IPrintAdapter.Stub.asInterface(mPrinable),
+ mPrintSpooler.generateFileForPrintJob(mPrintJobId));
+
+ try {
+ mPrinable.linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException re) {
+ finish();
+ }
+
+ mPrinterDiscoveryObserver = new PrintDiscoveryObserver(getMainLooper());
+
+ bindUi();
+ }
+
+ @Override
+ protected void onDestroy() {
+ mPrinable.unlinkToDeath(mDeathRecipient, 0);
+ super.onDestroy();
+ }
+
+ private void bindUi() {
+ // Copies
+ mCopiesEditText = (EditText) findViewById(R.id.copies_edittext);
+ mCopiesEditText.setText(String.valueOf(MIN_COPIES));
+ mCopiesEditText.addTextChangedListener(mTextWatcher);
+ mCopiesEditText.setFilters(new InputFilter[] {mInputFilter});
+
+ // Destination.
+ mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner);
+ mDestinationSpinnerAdapter = new ArrayAdapter<SpinnerItem<PrinterInfo>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter);
+ mDestinationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Media size.
+ mMediaSizeSpinner = (Spinner) findViewById(R.id.media_size_spinner);
+ mMediaSizeSpinnerAdapter = new ArrayAdapter<SpinnerItem<MediaSize>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter);
+ mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Resolution.
+ mResolutionSpinner = (Spinner) findViewById(R.id.resolution_spinner);
+ mResolutionSpinnerAdapter = new ArrayAdapter<SpinnerItem<Resolution>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mResolutionSpinner.setAdapter(mResolutionSpinnerAdapter);
+ mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Input tray.
+ mInputTraySpinner = (Spinner) findViewById(R.id.input_tray_spinner);
+ mInputTraySpinnerAdapter = new ArrayAdapter<SpinnerItem<Tray>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mInputTraySpinner.setAdapter(mInputTraySpinnerAdapter);
+
+ // Output tray.
+ mOutputTraySpinner = (Spinner) findViewById(R.id.output_tray_spinner);
+ mOutputTraySpinnerAdapter = new ArrayAdapter<SpinnerItem<Tray>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mOutputTraySpinner.setAdapter(mOutputTraySpinnerAdapter);
+ mOutputTraySpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Duplex mode.
+ mDuplexModeSpinner = (Spinner) findViewById(R.id.duplex_mode_spinner);
+ mDuplexModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mDuplexModeSpinner.setAdapter(mDuplexModeSpinnerAdapter);
+ mDuplexModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Color mode.
+ mColorModeSpinner = (Spinner) findViewById(R.id.color_mode_spinner);
+ mColorModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
+ mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Color mode.
+ mFittingModeSpinner = (Spinner) findViewById(R.id.fitting_mode_spinner);
+ mFittingModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mFittingModeSpinner.setAdapter(mFittingModeSpinnerAdapter);
+ mFittingModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Orientation
+ mOrientationSpinner = (Spinner) findViewById(R.id.orientation_spinner);
+ mOrientationSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
+ android.R.layout.simple_spinner_dropdown_item);
+ mOrientationSpinner.setAdapter(mOrientationSpinnerAdapter);
+ mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+ }
+
+ 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)));
+
+ // Media size.
+ mMediaSizeSpinnerAdapter.clear();
+ List<MediaSize> 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, mediaSize.getLabel(getPackageManager())));
+ }
+ final int selectedMediaSizeIndex = mediaSizes.indexOf(
+ mPrintAttributes.getMediaSize());
+ mMediaSizeSpinner.setOnItemSelectedListener(null);
+ mMediaSizeSpinner.setSelection(selectedMediaSizeIndex);
+ mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Resolution.
+ mResolutionSpinnerAdapter.clear();
+ List<Resolution> resolutions = printer.getResolutions();
+ final int resolutionCount = resolutions.size();
+ for (int i = 0; i < resolutionCount; i++) {
+ Resolution resolution = resolutions.get(i);
+ mResolutionSpinnerAdapter.add(new SpinnerItem<Resolution>(
+ resolution, resolution.getLabel(getPackageManager())));
+ }
+ final int selectedResolutionIndex = resolutions.indexOf(
+ mPrintAttributes.getResolution());
+ mResolutionSpinner.setOnItemSelectedListener(null);
+ mResolutionSpinner.setSelection(selectedResolutionIndex);
+ mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+ // Input tray.
+ mInputTraySpinnerAdapter.clear();
+ List<Tray> inputTrays = printer.getInputTrays();
+ final int inputTrayCount = inputTrays.size();
+ for (int i = 0; i < inputTrayCount; i++) {
+ Tray inputTray = inputTrays.get(i);
+ mInputTraySpinnerAdapter.add(new SpinnerItem<Tray>(
+ inputTray, inputTray.getLabel(getPackageManager())));
+ }
+ final int selectedInputTrayIndex = inputTrays.indexOf(
+ mPrintAttributes.getInputTray());
+ mInputTraySpinner.setSelection(selectedInputTrayIndex);
+
+ // Output tray.
+ mOutputTraySpinnerAdapter.clear();
+ List<Tray> outputTrays = printer.getOutputTrays();
+ final int outputTrayCount = outputTrays.size();
+ for (int i = 0; i < outputTrayCount; i++) {
+ Tray outputTray = outputTrays.get(i);
+ mOutputTraySpinnerAdapter.add(new SpinnerItem<Tray>(
+ outputTray, outputTray.getLabel(getPackageManager())));
+ }
+ final int selectedOutputTrayIndex = outputTrays.indexOf(
+ mPrintAttributes.getOutputTray());
+ mOutputTraySpinner.setSelection(selectedOutputTrayIndex);
+
+ // Duplex mode.
+ final int duplexModes = printer.getDuplexModes();
+ mDuplexModeSpinnerAdapter.clear();
+ String[] duplexModeLabels = getResources().getStringArray(
+ R.array.duplex_mode_labels);
+ int remainingDuplexModes = duplexModes;
+ while (remainingDuplexModes != 0) {
+ final int duplexBitOffset = Integer.numberOfTrailingZeros(remainingDuplexModes);
+ final int duplexMode = 1 << duplexBitOffset;
+ remainingDuplexModes &= ~duplexMode;
+ mDuplexModeSpinnerAdapter.add(new SpinnerItem<Integer>(duplexMode,
+ duplexModeLabels[duplexBitOffset]));
+ }
+ final int selectedDuplexModeIndex = Integer.numberOfTrailingZeros(
+ (duplexModes & mPrintAttributes.getDuplexMode()));
+ mDuplexModeSpinner.setSelection(selectedDuplexModeIndex);
+
+ // 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<Integer>(colorMode,
+ colorModeLabels[colorBitOffset]));
+ }
+ final int selectedColorModeIndex = Integer.numberOfTrailingZeros(
+ (colorModes & mPrintAttributes.getColorMode()));
+ mColorModeSpinner.setSelection(selectedColorModeIndex);
+
+ // Fitting mode.
+ final int fittingModes = printer.getFittingModes();
+ mFittingModeSpinnerAdapter.clear();
+ String[] fittingModeLabels = getResources().getStringArray(
+ R.array.fitting_mode_labels);
+ int remainingFittingModes = fittingModes;
+ while (remainingFittingModes != 0) {
+ final int fittingBitOffset = Integer.numberOfTrailingZeros(remainingFittingModes);
+ final int fittingMode = 1 << fittingBitOffset;
+ remainingFittingModes &= ~fittingMode;
+ mFittingModeSpinnerAdapter.add(new SpinnerItem<Integer>(fittingMode,
+ fittingModeLabels[fittingBitOffset]));
+ }
+ final int selectedFittingModeIndex = Integer.numberOfTrailingZeros(
+ (fittingModes & mPrintAttributes.getFittingMode()));
+ mFittingModeSpinner.setSelection(selectedFittingModeIndex);
+
+ // 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<Integer>(orientation,
+ orientationLabels[orientationBitOffset]));
+ }
+ final int selectedOrientationIndex = Integer.numberOfTrailingZeros(
+ (orientations & mPrintAttributes.getOrientation()));
+ mOrientationSpinner.setSelection(selectedOrientationIndex);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ try {
+ mPrintManager.startDiscoverPrinters(mPrinterDiscoveryObserver);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error starting printer discovery!", re);
+ }
+ notifyPrintableStartIfNeeded();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ try {
+ mPrintManager.stopDiscoverPrinters();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error starting printer discovery!", re);
+ }
+ notifyPrintableFinishIfNeeded();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.print_job_config_activity, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.print_button) {
+ mPrintConfirmed = true;
+ finish();
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void notifyPrintableStartIfNeeded() {
+ if (mDestinationSpinner.getSelectedItemPosition() < 0
+ || mPrintStarted) {
+ return;
+ }
+ mPrintStarted = true;
+ new QueuedAsyncTask<Void>(mTaskQueue) {
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ mRemotePrintAdapter.start();
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error reading printed data!", ioe);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ updatePrintableContentIfNeeded();
+ }
+ }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+
+ private void updatePrintableContentIfNeeded() {
+ if (!mPrintStarted) {
+ return;
+ }
+
+ mPrintSpooler.setPrintJobAttributes(mPrintJobId, mPrintAttributes);
+
+ // TODO: Implement page selector.
+ final List<PageRange> pages = new ArrayList<PageRange>();
+ pages.add(PageRange.ALL_PAGES);
+
+ new QueuedAsyncTask<File>(mTaskQueue) {
+ @Override
+ protected File doInBackground(Void... params) {
+ try {
+ mRemotePrintAdapter.printAttributesChanged(mPrintAttributes);
+ mRemotePrintAdapter.cancelPrint();
+ mRemotePrintAdapter.print(pages);
+ return mRemotePrintAdapter.getFile();
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error reading printed data!", ioe);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(File file) {
+ super.onPostExecute(file);
+ updatePrintPreview(file);
+ }
+ }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+
+ private void notifyPrintableFinishIfNeeded() {
+ if (!mPrintStarted) {
+ return;
+ }
+ mPrintStarted = false;
+
+ // Cancel all pending async tasks if the activity was canceled.
+ if (!mPrintConfirmed) {
+ final int taskCount = mTaskQueue.size();
+ for (int i = taskCount - 1; i >= 0; i--) {
+ mTaskQueue.remove(i).cancel();
+ }
+ }
+
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ // Notify the app that printing completed.
+ try {
+ mRemotePrintAdapter.finish();
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error reading printed data!", ioe);
+ }
+
+ // If canceled, nothing to do.
+ if (!mPrintConfirmed) {
+ mPrintSpooler.setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_CANCELED);
+ return null;
+ }
+
+ // No printer, nothing to do.
+ final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
+ if (selectedIndex < 0) {
+ // Update the print job's status.
+ mPrintSpooler.setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_CANCELED);
+ return null;
+ }
+
+ // Update the print job's printer.
+ SpinnerItem<PrinterInfo> printerItem =
+ mDestinationSpinnerAdapter.getItem(selectedIndex);
+ PrinterId printerId = printerItem.value.getId();
+ mPrintSpooler.setPrintJobPrinterId(mPrintJobId, printerId);
+
+ // Update the print job's status.
+ mPrintSpooler.setPrintJobState(mPrintJobId,
+ PrintJobInfo.STATE_QUEUED);
+ return null;
+ }
+
+ // Important: If we are canceling, then we do not wait for the write
+ // to complete since the result will be discarded anyway, we simply
+ // execute the finish immediately which will interrupt the write.
+ }.executeOnExecutor(mPrintConfirmed ? AsyncTask.SERIAL_EXECUTOR
+ : AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+
+ if (DEBUG) {
+ if (mPrintConfirmed) {
+ File file = mRemotePrintAdapter.getFile();
+ if (file.exists()) {
+ new ViewSpooledFileAsyncTask(file).executeOnExecutor(
+ AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+ }
+ }
+ }
+
+ private void updatePrintPreview(File file) {
+ // TODO: Implement
+ }
+
+ private void addPrinters(List<PrinterInfo> 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<PrinterInfo>(
+ addedPrinter, addedPrinter.getLabel()));
+ } else {
+ Log.w(LOG_TAG, "Skipping a duplicate printer: " + addedPrinter);
+ }
+ }
+ }
+
+ private void removePrinters(List<PrinterId> 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 abstract class QueuedAsyncTask<T> extends AsyncTask<Void, Void, T> {
+
+ private final List<QueuedAsyncTask<?>> mPendingOrRunningTasks;
+
+ public QueuedAsyncTask(List<QueuedAsyncTask<?>> pendingOrRunningTasks) {
+ mPendingOrRunningTasks = pendingOrRunningTasks;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ mPendingOrRunningTasks.add(this);
+ }
+
+ @Override
+ protected void onPostExecute(T result) {
+ mPendingOrRunningTasks.remove(this);
+ }
+
+ public void cancel() {
+ super.cancel(true);
+ mPendingOrRunningTasks.remove(this);
+ }
+ }
+
+ private final class ViewSpooledFileAsyncTask extends AsyncTask<Void, Void, Void> {
+
+ private final File mFile;
+
+ public ViewSpooledFileAsyncTask(File file) {
+ mFile = file;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ mFile.setExecutable(true, false);
+ mFile.setWritable(true, false);
+ mFile.setReadable(true, false);
+
+ 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;
+ }
+ }
+
+ 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;
+
+ private final Handler mHandler;
+
+ @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;
+ addPrinters(printers);
+ // Just added the first printer, so select it and start printing.
+ if (mDestinationSpinnerAdapter.getCount() == 1) {
+ mDestinationSpinner.setSelection(0);
+ }
+ } break;
+ case MESSAGE_REMOVE_DICOVERED_PRINTERS: {
+ List<PrinterId> printerIds = (List<PrinterId>) message.obj;
+ removePrinters(printerIds);
+ // TODO: Handle removing the last printer.
+ } break;
+ }
+ }
+ };
+ }
+
+ @Override
+ public void addDiscoveredPrinters(List<PrinterInfo> printers) {
+ mHandler.obtainMessage(MESSAGE_ADD_DICOVERED_PRINTERS, printers).sendToTarget();
+ }
+
+ @Override
+ public void removeDiscoveredPrinters(List<PrinterId> printers) {
+ mHandler.obtainMessage(MESSAGE_REMOVE_DICOVERED_PRINTERS, printers).sendToTarget();
+ }
+ }
+
+ 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();
+ }
+ }
+}