blob: c0faed8afbf65038b20201895157d6a1672cb930 [file] [log] [blame]
/*
* 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.IBinder.DeathRecipient;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.UserHandle;
import android.print.IPrintDocumentAdapter;
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.PrintDocumentAdapter.LayoutResultCallback;
import android.print.PrintDocumentAdapter.WriteResultCallback;
import android.print.PrintDocumentInfo;
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.Choreographer;
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.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 PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this);
private IPrinterDiscoveryObserver mPrinterDiscoveryObserver;
private int mAppId;
private int mPrintJobId;
private PrintAttributes mPrintAttributes;
private RemotePrintDocumentAdapter 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 mPrintConfirmed;
private boolean mStarted;
private IBinder mIPrintDocumentAdapter;
// 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);
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(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()));
}
final int selectedMediaSizeIndex = mediaSizes.indexOf(
mPrintAttributes.getMediaSize());
mMediaSizeSpinner.setOnItemSelectedListener(null);
mMediaSizeSpinner.setSelection(selectedMediaSizeIndex);
// 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);
// AdapterView has the weird behavior to notify the selection listener for a
// selection event that occurred *before* the listener was registered because
// it does the real selection change on the next layout pass. To avoid this
// behavior we re-attach the listener in the next traversal window - fun!
Choreographer.getInstance().postCallback(
Choreographer.CALLBACK_TRAVERSAL, new Runnable() {
@Override
public void run() {
mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
}
}, null);
// Input tray.
mInputTraySpinnerAdapter.clear();
List<Tray> inputTrays = printer.getInputTrays();
if (inputTrays != null) {
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();
if (outputTrays != null) {
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();
mPrintSpooler.startPrinterDiscovery(mPrinterDiscoveryObserver);
notifyPrintableStartIfNeeded();
}
@Override
protected void onPause() {
super.onPause();
mPrintSpooler.stopPrinterDiscovery();
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
|| mStarted) {
return;
}
mStarted = true;
mRemotePrintAdapter.start();
updatePrintableContentIfNeeded();
}
private void updatePrintableContentIfNeeded() {
if (!mStarted) {
return;
}
// TODO: Implement old attributes tracking
mPrintSpooler.setPrintJobAttributes(mPrintJobId, mPrintAttributes);
// TODO: Implement setting the print preview attribute
mRemotePrintAdapter.layout(new PrintAttributes.Builder().create(),
mPrintAttributes, new LayoutResultCallback() {
@Override
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
// TODO: Handle the case of unchanged content
mPrintSpooler.setPrintJobPrintDocumentInfo(mPrintJobId, info);
// TODO: Implement page selector.
final List<PageRange> pages = new ArrayList<PageRange>();
pages.add(PageRange.ALL_PAGES);
mRemotePrintAdapter.write(pages, new WriteResultCallback() {
@Override
public void onWriteFinished(List<PageRange> pages) {
updatePrintPreview(mRemotePrintAdapter.getFile());
}
@Override
public void onWriteFailed(CharSequence error) {
Log.e(LOG_TAG, "Error write layout: " + error);
finishActivity(Activity.RESULT_CANCELED);
}
});
}
@Override
public void onLayoutFailed(CharSequence error) {
Log.e(LOG_TAG, "Error during layout: " + error);
finishActivity(Activity.RESULT_CANCELED);
}
}, new Bundle());
}
private void notifyPrintableFinishIfNeeded() {
if (!mStarted) {
return;
}
if (!mPrintConfirmed) {
mRemotePrintAdapter.cancel();
}
mRemotePrintAdapter.finish();
// If canceled or no printer, nothing to do.
final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
if (!mPrintConfirmed || selectedIndex < 0) {
// Update the print job's status.
mPrintSpooler.setPrintJobState(mPrintJobId,
PrintJobInfo.STATE_CANCELED);
return;
}
// 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);
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);
}
}
}
// Caution: Use this only for debugging
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();
}
}
}