am 59703c71: Merge "Partial implementation for the favorite and available printer tracking." into klp-dev

* commit '59703c7186ea49669e7dd326a1e2e704385cbaff':
  Partial implementation for the favorite and available printer tracking.
This commit is contained in:
Svetoslav
2013-08-14 07:58:43 -07:00
committed by Android Git Automerger
12 changed files with 959 additions and 290 deletions

View File

@@ -50,6 +50,12 @@
android:theme="@style/PrintJobConfigActivityTheme">
</activity>
<activity
android:name=".ChoosePrinterActivity"
android:exported="false"
android:theme="@android:style/Theme.Holo.Light">
</activity>
<receiver
android:name=".NotificationController$NotificationBroadcastReceiver"
android:exported="false" >

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
</ListView>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_search"
android:title="@string/search"
android:icon="@*android:drawable/ic_menu_search_holo_light"
android:actionViewClass="android.widget.SearchView"
android:showAsAction="ifRoom"
android:alphabeticShortcut="f"
android:imeOptions="actionSearch">
</item>
</menu>

View File

@@ -58,6 +58,11 @@
<!-- Title for the temporary dialog show while an app is generating a print job. [CHAR LIMIT=30] -->
<string name="generating_print_job">Generating print job</string>
<!-- Choose printer activity -->
<!-- Title for the share action bar menu item. [CHAR LIMIT=20] -->
<string name="search">Search</string>
<!-- Notifications -->
<!-- Template for the notificaiton label for a printing print job. [CHAR LIMIT=25] -->

View File

@@ -0,0 +1,285 @@
/*
* 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.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.print.IPrinterDiscoverySessionController;
import android.print.IPrinterDiscoverySessionObserver;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.util.ArraySet;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* This class is responsible to provide the available printers.
* It starts and stops printer discovery and manages the returned
* printers.
*/
public class AvailablePrinterProvider extends DataProvider<PrinterInfo>
implements DataLoader {
private static final String LOG_TAG = "AvailablePrinterProvider";
private final Set<PrinterId> mPrinteIdsSet = new ArraySet<PrinterId>();
private final List<PrinterInfo> mPrinters = new ArrayList<PrinterInfo>();
private final List<PrinterId> mPriorityList;
private PrinterDiscoverySession mDiscoverySession;
public AvailablePrinterProvider(Context context, List<PrinterId> priorityList) {
mDiscoverySession = new PrinterDiscoverySession(context.getMainLooper());
mPriorityList = priorityList;
}
@Override
public void startLoadData() {
mDiscoverySession.open();
}
@Override
public void stopLoadData() {
mDiscoverySession.close();
}
@Override
public int getItemCount() {
return mPrinters.size();
}
@Override
public int getItemIndex(PrinterInfo printer) {
return mPrinters.indexOf(printer);
}
@Override
public PrinterInfo getItemAt(int index) {
return mPrinters.get(index);
}
public void refreshItem(int index) {
PrinterInfo printer = getItemAt(index);
mDiscoverySession.requestPrinterUpdate(printer.getId());
}
private void addPrinters(List<PrinterInfo> printers) {
boolean addedPrinters = false;
final int addedPrinterCount = printers.size();
for (int i = 0; i < addedPrinterCount; i++) {
PrinterInfo addedPrinter = printers.get(i);
if (mPrinteIdsSet.add(addedPrinter.getId())) {
mPrinters.add(addedPrinter);
addedPrinters = true;
}
}
if (addedPrinters) {
notifyChanged();
}
}
private void updatePrinters(List<PrinterInfo> printers) {
boolean updatedPrinters = false;
final int updatedPrinterCount = printers.size();
for (int i = 0; i < updatedPrinterCount; i++) {
PrinterInfo updatedPrinter = printers.get(i);
if (mPrinteIdsSet.contains(updatedPrinter.getId())) {
final int oldPrinterCount = mPrinters.size();
for (int j = 0; j < oldPrinterCount; j++) {
PrinterInfo oldPrinter = mPrinters.get(j);
if (updatedPrinter.getId().equals(oldPrinter.getId())) {
mPrinters.set(j, updatedPrinter);
updatedPrinters = true;
break;
}
}
}
}
if (updatedPrinters) {
notifyChanged();
}
}
private void removePrinters(List<PrinterId> printers) {
boolean removedPrinters = false;
final int removedPrinterCount = printers.size();
for (int i = 0; i < removedPrinterCount; i++) {
PrinterId removedPrinter = printers.get(i);
if (mPrinteIdsSet.contains(removedPrinter)) {
mPrinteIdsSet.remove(removedPrinter);
Iterator<PrinterInfo> iterator = mPrinters.iterator();
while (iterator.hasNext()) {
PrinterInfo oldPrinter = iterator.next();
if (removedPrinter.equals(oldPrinter.getId())) {
iterator.remove();
break;
}
}
}
}
if (removedPrinters) {
notifyChanged();
}
}
private final class PrinterDiscoverySession {
private final Handler mHandler;
private final IPrinterDiscoverySessionObserver mObserver;
private IPrinterDiscoverySessionController mController;
public PrinterDiscoverySession(Looper looper) {
mHandler = new SessionHandler(looper);
mObserver = new PrinterDiscoverySessionObserver(this);
}
public void open() {
PrintSpooler.peekInstance().createPrinterDiscoverySession(
mObserver);
}
public void close() {
if (mController != null) {
try {
mController.close();
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error closing printer discovery session", re);
} finally {
mController = null;
}
}
}
public void requestPrinterUpdate(PrinterId printerId) {
if (mController != null) {
try {
mController.requestPrinterUpdate(printerId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error requesting printer udpdate", re);
}
}
}
private final class SessionHandler extends Handler {
public static final int MSG_SET_CONTROLLER = 1;
public static final int MSG_ON_PRINTERS_ADDED = 2;
public static final int MSG_ON_PRINTERS_REMOVED = 3;
public static final int MSG_ON_PRINTERS_UPDATED = 4;
public SessionHandler(Looper looper) {
super(looper, null, false);
}
@Override
@SuppressWarnings("unchecked")
public void handleMessage(Message message) {
switch (message.what) {
case MSG_SET_CONTROLLER: {
mController = (IPrinterDiscoverySessionController) message.obj;
try {
mController.open(mPriorityList);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error starting printer discovery");
}
} break;
case MSG_ON_PRINTERS_ADDED: {
List<PrinterInfo> printers = (List<PrinterInfo>) message.obj;
addPrinters(printers);
} break;
case MSG_ON_PRINTERS_REMOVED: {
List<PrinterId> printers = (List<PrinterId>) message.obj;
removePrinters(printers);
} break;
case MSG_ON_PRINTERS_UPDATED: {
List<PrinterInfo> printers = (List<PrinterInfo>) message.obj;
updatePrinters(printers);
} break;
};
}
}
}
private static final class PrinterDiscoverySessionObserver
extends IPrinterDiscoverySessionObserver.Stub {
private final WeakReference<PrinterDiscoverySession> mWeakSession;
public PrinterDiscoverySessionObserver(PrinterDiscoverySession session) {
mWeakSession = new WeakReference<PrinterDiscoverySession>(session);
}
@Override
public void setController(IPrinterDiscoverySessionController controller) {
PrinterDiscoverySession sesison = mWeakSession.get();
if (sesison != null) {
sesison.mHandler.obtainMessage(
PrinterDiscoverySession.SessionHandler.MSG_SET_CONTROLLER,
controller).sendToTarget();
}
}
@Override
public void onPrintersAdded(List<PrinterInfo> printers) {
PrinterDiscoverySession sesison = mWeakSession.get();
if (sesison != null) {
sesison.mHandler.obtainMessage(
PrinterDiscoverySession.SessionHandler.MSG_ON_PRINTERS_ADDED,
printers).sendToTarget();
}
}
@Override
public void onPrintersRemoved(List<PrinterId> printers) {
PrinterDiscoverySession session = mWeakSession.get();
if (session != null) {
session.mHandler.obtainMessage(
PrinterDiscoverySession.SessionHandler.MSG_ON_PRINTERS_REMOVED,
printers).sendToTarget();
}
}
@Override
public void onPrintersUpdated(List<PrinterInfo> printers) {
PrinterDiscoverySession session = mWeakSession.get();
if (session != null) {
session.mHandler.obtainMessage(
PrinterDiscoverySession.SessionHandler.MSG_ON_PRINTERS_UPDATED,
printers).sendToTarget();
}
}
};
}

View File

@@ -0,0 +1,28 @@
/*
* 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.os.Bundle;
public class ChoosePrinterActivity extends Activity {
@Override
public void onCreate(Bundle bundle) {
setContentView(R.layout.choose_printer_activity);
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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;
/**
* This is the contract for a class that know how to load data.
*/
public interface DataLoader {
/**
* Requests to start loading data.
*/
public void startLoadData();
/**
* Requests to stop loading data.
*/
public void stopLoadData();
}

View File

@@ -0,0 +1,50 @@
/*
* 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.database.DataSetObservable;
/**
* This is the simple contract for data providers.
*
* @param <T> The type of the providers data.
*/
public abstract class DataProvider<T> extends DataSetObservable {
/**
* Gets the number of items.
*
* @return The item count.
*/
public abstract int getItemCount();
/**
* Gets the index of an item.
*
* @param item The item.
* @return The item index.
*/
public abstract int getItemIndex(T item);
/**
* Gets an item at a given position.
*
* @param index The position.
* @return The item.
*/
public abstract T getItemAt(int index);
}

View File

@@ -0,0 +1,364 @@
/*
* 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.content.ComponentName;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Build;
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* This class provides the favorite printers based on past usage.
*/
final class FavoritePrinterProvider extends DataProvider<PrinterInfo> implements DataLoader {
private static final String LOG_TAG = "FavoritePrinterProvider";
private static final boolean DEBUG = true && Build.IS_DEBUGGABLE;
private static final int MAX_HISTORY_LENGTH = 50;
private static final double WEIGHT_DECAY_COEFFICIENT = 0.95f;
private final List<PrinterRecord> mHistoricalPrinters = new ArrayList<PrinterRecord>();
private final List<PrinterRecord> mFavoritePrinters = new ArrayList<PrinterRecord>();
private final PersistenceManager mPersistenceManager;
public FavoritePrinterProvider(Context context) {
mPersistenceManager = new PersistenceManager(context);
}
public void addPrinter(PrinterInfo printer) {
addPrinterInternal(printer);
computeFavoritePrinters();
mPersistenceManager.writeState();
}
@Override
public int getItemCount() {
return mFavoritePrinters.size();
}
@Override
public PrinterInfo getItemAt(int index) {
return mFavoritePrinters.get(index).printer;
}
@Override
public int getItemIndex(PrinterInfo printer) {
return mFavoritePrinters.indexOf(printer);
}
@Override
public void startLoadData() {
mPersistenceManager.readStateLocked();
computeFavoritePrinters();
}
@Override
public void stopLoadData() {
/* do nothing */
}
private void addPrinterInternal(PrinterInfo printer) {
if (mHistoricalPrinters.size() >= MAX_HISTORY_LENGTH) {
mHistoricalPrinters.remove(0);
}
mHistoricalPrinters.add(new PrinterRecord(printer));
}
private void computeFavoritePrinters() {
Map<PrinterId, PrinterRecord> recordMap =
new ArrayMap<PrinterId, PrinterRecord>();
// Recompute the weights.
float currentWeight = 1.0f;
final int printerCount = mHistoricalPrinters.size();
for (int i = printerCount - 1; i >= 0; i--) {
PrinterRecord record = mHistoricalPrinters.get(i);
record.weight = currentWeight;
// Aggregate weight for the same printer
PrinterRecord oldRecord = recordMap.put(record.printer.getId(), record);
if (oldRecord != null) {
record.weight += oldRecord.weight;
}
currentWeight *= WEIGHT_DECAY_COEFFICIENT;
}
// Copy the unique printer records with computed weights.
mFavoritePrinters.addAll(recordMap.values());
// Soft the favorite printers.
Collections.sort(mFavoritePrinters);
}
private final class PrinterRecord implements Comparable<PrinterRecord> {
public final PrinterInfo printer;
public float weight;
public PrinterRecord(PrinterInfo printer) {
this.printer = printer;
}
@Override
public int compareTo(PrinterRecord another) {
return Float.floatToIntBits(another.weight) - Float.floatToIntBits(weight);
}
}
private final class PersistenceManager {
private static final String PERSIST_FILE_NAME = "printer_history.xml";
private static final String TAG_PRINTERS = "printers";
private static final String TAG_PRINTER = "printer";
private static final String TAG_PRINTER_ID = "printerId";
private static final String ATTR_LOCAL_ID = "localId";
private static final String ATTR_SERVICE_NAME = "serviceName";
private static final String ATTR_NAME = "name";
private static final String ATTR_DESCRIPTION = "description";
private static final String ATTR_STATUS = "status";
private final AtomicFile mStatePersistFile;
private PersistenceManager(Context context) {
mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
PERSIST_FILE_NAME));
}
@SuppressWarnings("unchecked")
public void writeState() {
new AsyncTask<List<PrinterRecord>, Void, Void>() {
@Override
protected Void doInBackground(List<PrinterRecord>... printers) {
doWriteState(printers[0]);
return null;
}
@Override
protected void onPostExecute(Void result) {
notifyChanged();
}
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
new ArrayList<PrinterRecord>(mHistoricalPrinters));
}
private void doWriteState(List<PrinterRecord> printers) {
FileOutputStream out = null;
try {
out = mStatePersistFile.startWrite();
XmlSerializer serializer = new FastXmlSerializer();
serializer.setOutput(out, "utf-8");
serializer.startDocument(null, true);
serializer.startTag(null, TAG_PRINTERS);
final int printerCount = printers.size();
for (int i = printerCount - 1; i >= 0; i--) {
PrinterInfo printer = printers.get(i).printer;
serializer.startTag(null, TAG_PRINTER);
serializer.attribute(null, ATTR_NAME, printer.getName());
serializer.attribute(null, ATTR_STATUS, String.valueOf(printer.getStatus()));
String description = printer.getDescription();
if (description != null) {
serializer.attribute(null, ATTR_DESCRIPTION, description);
}
PrinterId printerId = printer.getId();
serializer.startTag(null, TAG_PRINTER_ID);
serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
.flattenToString());
serializer.endTag(null, TAG_PRINTER_ID);
serializer.endTag(null, TAG_PRINTER);
if (DEBUG) {
Log.i(LOG_TAG, "[PERSISTED] " + printer);
}
}
serializer.endTag(null, TAG_PRINTERS);
serializer.endDocument();
mStatePersistFile.finishWrite(out);
if (DEBUG) {
Log.i(LOG_TAG, "[PERSIST END]");
}
} catch (IOException ioe) {
Slog.w(LOG_TAG, "Failed to write printer history, restoring backup.", ioe);
mStatePersistFile.failWrite(out);
} finally {
IoUtils.closeQuietly(out);
}
}
public void readStateLocked() {
FileInputStream in = null;
try {
in = mStatePersistFile.openRead();
} catch (FileNotFoundException e) {
Log.i(LOG_TAG, "No existing printer history.");
return;
}
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
parseState(parser);
} catch (IllegalStateException ise) {
Slog.w(LOG_TAG, "Failed parsing ", ise);
} catch (NullPointerException npe) {
Slog.w(LOG_TAG, "Failed parsing ", npe);
} catch (NumberFormatException nfe) {
Slog.w(LOG_TAG, "Failed parsing ", nfe);
} catch (XmlPullParserException xppe) {
Slog.w(LOG_TAG, "Failed parsing ", xppe);
} catch (IOException ioe) {
Slog.w(LOG_TAG, "Failed parsing ", ioe);
} catch (IndexOutOfBoundsException iobe) {
Slog.w(LOG_TAG, "Failed parsing ", iobe);
} finally {
IoUtils.closeQuietly(in);
}
notifyChanged();
}
private void parseState(XmlPullParser parser)
throws IOException, XmlPullParserException {
parser.next();
skipEmptyTextTags(parser);
expect(parser, XmlPullParser.START_TAG, TAG_PRINTERS);
parser.next();
while (parsePrinter(parser)) {
parser.next();
}
skipEmptyTextTags(parser);
expect(parser, XmlPullParser.END_TAG, TAG_PRINTERS);
// We were reading the new records first and appended them first,
// hence the historical list is in a reversed order, so fix that.
Collections.reverse(mHistoricalPrinters);
}
private boolean parsePrinter(XmlPullParser parser)
throws IOException, XmlPullParserException {
skipEmptyTextTags(parser);
if (!accept(parser, XmlPullParser.START_TAG, TAG_PRINTER)) {
return false;
}
String name = parser.getAttributeValue(null, ATTR_NAME);
String description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
final int status = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATUS));
parser.next();
skipEmptyTextTags(parser);
expect(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID);
String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
null, ATTR_SERVICE_NAME));
PrinterId printerId = new PrinterId(service, localId);
parser.next();
skipEmptyTextTags(parser);
expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
parser.next();
PrinterInfo.Builder builder = new PrinterInfo.Builder(printerId, name, status);
builder.setDescription(description);
PrinterInfo printer = builder.create();
addPrinterInternal(printer);
if (DEBUG) {
Log.i(LOG_TAG, "[RESTORED] " + printer);
}
skipEmptyTextTags(parser);
expect(parser, XmlPullParser.END_TAG, TAG_PRINTER);
return true;
}
private void expect(XmlPullParser parser, int type, String tag)
throws IOException, XmlPullParserException {
if (!accept(parser, type, tag)) {
throw new XmlPullParserException("Exepected event: " + type
+ " and tag: " + tag + " but got event: " + parser.getEventType()
+ " and tag:" + parser.getName());
}
}
private void skipEmptyTextTags(XmlPullParser parser)
throws IOException, XmlPullParserException {
while (accept(parser, XmlPullParser.TEXT, null)
&& "\n".equals(parser.getText())) {
parser.next();
}
}
private boolean accept(XmlPullParser parser, int type, String tag)
throws IOException, XmlPullParserException {
if (parser.getEventType() != type) {
return false;
}
if (tag != null) {
if (!tag.equals(parser.getName())) {
return false;
}
} else if (parser.getName() != null) {
return false;
}
return true;
}
}
}

View File

@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -34,8 +35,6 @@ import android.os.Message;
import android.os.RemoteException;
import android.print.ILayoutResultCallback;
import android.print.IPrintDocumentAdapter;
import android.print.IPrinterDiscoverySessionController;
import android.print.IPrinterDiscoverySessionObserver;
import android.print.IWriteResultCallback;
import android.print.PageRange;
import android.print.PrintAttributes;
@@ -64,6 +63,7 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
@@ -72,7 +72,6 @@ import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -132,11 +131,13 @@ public class PrintJobConfigActivity extends Activity {
}
};
private PrintSpooler mSpooler;
private Editor mEditor;
private Document mDocument;
private PrintController mController;
private PrinterDiscoverySessionObserver mPrinterDiscoverySessionObserver;
private AvailablePrinterProvider mAvailablePrinters;
private FavoritePrinterProvider mFavoritePrinters;
private int mPrintJobId;
@@ -168,12 +169,15 @@ public class PrintJobConfigActivity extends Activity {
mCurrPrintAttributes.copyFrom(attributes);
}
mSpooler = PrintSpooler.peekInstance();
// TODO: Use history
mAvailablePrinters = new AvailablePrinterProvider(this, null);
mFavoritePrinters = new FavoritePrinterProvider(this);
mEditor = new Editor();
mDocument = new Document();
mController = new PrintController(new RemotePrintDocumentAdapter(
IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter),
mSpooler.generateFileForPrintJob(mPrintJobId)));
PrintSpooler.peekInstance().generateFileForPrintJob(mPrintJobId)));
try {
mIPrintDocumentAdapter.linkToDeath(mDeathRecipient, 0);
@@ -184,9 +188,26 @@ public class PrintJobConfigActivity extends Activity {
mController.initialize();
mEditor.initialize();
mPrinterDiscoverySessionObserver = new PrinterDiscoverySessionObserver(mEditor,
getMainLooper());
mSpooler.createPrinterDiscoverySession(mPrinterDiscoverySessionObserver);
}
@Override
protected void onResume() {
super.onResume();
// TODO: Polish this
if (!mEditor.isPrintConfirmed()) {
mAvailablePrinters.startLoadData();
mFavoritePrinters.startLoadData();
}
}
@Override
protected void onPause() {
// TODO: Polish this
if (!mEditor.isPrintConfirmed()) {
mAvailablePrinters.stopLoadData();
mFavoritePrinters.stopLoadData();
}
super.onPause();
}
@Override
@@ -194,17 +215,14 @@ public class PrintJobConfigActivity extends Activity {
// We can safely do the work in here since at this point
// the system is bound to our (spooler) process which
// guarantees that this process will not be killed.
mPrinterDiscoverySessionObserver.close();
mPrinterDiscoverySessionObserver.destroy();
mPrinterDiscoverySessionObserver = null;
if (mController.hasStarted()) {
mController.finish();
}
if (mEditor.isPrintConfirmed() && mController.isFinished()) {
mSpooler.setPrintJobState(mPrintJobId,
PrintSpooler.peekInstance().setPrintJobState(mPrintJobId,
PrintJobInfo.STATE_QUEUED, null);
} else {
mSpooler.setPrintJobState(mPrintJobId,
PrintSpooler.peekInstance().setPrintJobState(mPrintJobId,
PrintJobInfo.STATE_CANCELED, null);
}
mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
@@ -320,7 +338,8 @@ public class PrintJobConfigActivity extends Activity {
// completed and nothing changed, so we handle writing as usual.
handleOnLayoutFinished(mDocument.info, false, mRequestCounter.get());
} else {
mSpooler.setPrintJobAttributesNoPersistence(mPrintJobId, mCurrPrintAttributes);
PrintSpooler.peekInstance().setPrintJobAttributesNoPersistence(mPrintJobId,
mCurrPrintAttributes);
mMetadata.putBoolean(PrintDocumentAdapter.METADATA_KEY_PRINT_PREVIEW,
!mEditor.isPrintConfirmed());
@@ -353,15 +372,14 @@ public class PrintJobConfigActivity extends Activity {
}
mControllerState = CONTROLLER_STATE_LAYOUT_COMPLETED;
mEditor.updateUi();
// If the info changed, we update the document and the print job,
// and update the UI since the the page range selection may have
// become invalid.
// If the info changed, we update the document and the print job.
final boolean infoChanged = !info.equals(mDocument.info);
if (infoChanged) {
mDocument.info = info;
mSpooler.setPrintJobPrintDocumentInfoNoPersistence(mPrintJobId, info);
mEditor.updateUi();
PrintSpooler.peekInstance().setPrintJobPrintDocumentInfoNoPersistence(
mPrintJobId, info);
}
// If the document info or the layout changed, then
@@ -447,11 +465,13 @@ public class PrintJobConfigActivity extends Activity {
if (Arrays.equals(mDocument.pages, mRequestedPages)) {
// We got a document with exactly the pages we wanted. Hence,
// the printer has to print all pages in the data.
mSpooler.setPrintJobPagesNoPersistence(mPrintJobId, ALL_PAGES_ARRAY);
PrintSpooler.peekInstance().setPrintJobPagesNoPersistence(mPrintJobId,
ALL_PAGES_ARRAY);
} else if (Arrays.equals(mDocument.pages, ALL_PAGES_ARRAY)) {
// We requested specific pages but got all of them. Hence,
// the printer has to print only the requested pages.
mSpooler.setPrintJobPagesNoPersistence(mPrintJobId, mRequestedPages);
PrintSpooler.peekInstance().setPrintJobPagesNoPersistence(mPrintJobId,
mRequestedPages);
} else if (PageRangeUtils.contains(mDocument.pages, mRequestedPages)) {
// We requested specific pages and got more but not all pages.
// Hence, we have to offset appropriately the printed pages to
@@ -460,14 +480,16 @@ public class PrintJobConfigActivity extends Activity {
final int offset = mDocument.pages[0].getStart() - pages[0].getStart();
PageRange[] offsetPages = Arrays.copyOf(mDocument.pages, mDocument.pages.length);
PageRangeUtils.offsetStart(offsetPages, offset);
mSpooler.setPrintJobPagesNoPersistence(mPrintJobId, offsetPages);
PrintSpooler.peekInstance().setPrintJobPagesNoPersistence(mPrintJobId,
offsetPages);
} else if (Arrays.equals(mRequestedPages, ALL_PAGES_ARRAY)
&& mDocument.pages.length == 1 && mDocument.pages[0].getStart() == 0
&& mDocument.pages[0].getEnd() == mDocument.info.getPageCount() - 1) {
// We requested all pages via the special constant and got all
// of them as an explicit enumeration. Hence, the printer has
// to print only the requested pages.
mSpooler.setPrintJobPagesNoPersistence(mPrintJobId, mDocument.pages);
PrintSpooler.peekInstance().setPrintJobPagesNoPersistence(mPrintJobId,
mDocument.pages);
} else {
// We did not get the pages we requested, then the application
// misbehaves, so we fail quickly.
@@ -592,7 +614,7 @@ public class PrintJobConfigActivity extends Activity {
private final EditText mRangeEditText;
private final Spinner mDestinationSpinner;
private final ArrayAdapter<SpinnerItem<PrinterInfo>> mDestinationSpinnerAdapter;
private final DestinationAdapter mDestinationSpinnerAdapter;
private final Spinner mMediaSizeSpinner;
private final ArrayAdapter<SpinnerItem<MediaSize>> mMediaSizeSpinnerAdapter;
@@ -623,15 +645,18 @@ public class PrintJobConfigActivity extends Activity {
return;
}
mCurrPrintAttributes.clear();
SpinnerItem<PrinterInfo> dstItem = mDestinationSpinnerAdapter.getItem(position);
if (dstItem != null) {
PrinterInfo printer = dstItem.value;
mSpooler.setPrintJobPrinterNoPersistence(mPrintJobId, printer);
PrinterInfo printer = (PrinterInfo) mDestinationSpinnerAdapter
.getItem(position);
if (printer != null) {
PrintSpooler.peekInstance().setPrintJobPrinterNoPersistence(
mPrintJobId, printer);
PrinterCapabilitiesInfo capabilities = printer.getCapabilities();
if (capabilities == null) {
List<PrinterId> printerIds = new ArrayList<PrinterId>();
printerIds.add(printer.getId());
mPrinterDiscoverySessionObserver.requestPrinterUpdate(printer.getId());
final int index = mAvailablePrinters.getItemIndex(printer);
mAvailablePrinters.refreshItem(index);
mWaitingForPrinterCapabilities = true;
//TODO: We need a timeout for the update.
} else {
capabilities.getDefaults(mCurrPrintAttributes);
@@ -728,7 +753,7 @@ public class PrintJobConfigActivity extends Activity {
}
mCopiesEditText.setError(null);
mSpooler.setPrintJobCopiesNoPersistence(mPrintJobId, copies);
PrintSpooler.peekInstance().setPrintJobCopiesNoPersistence(mPrintJobId, copies);
updateUi();
if (hadErrors && !hasErrors() && printAttributesChanged()) {
@@ -805,6 +830,8 @@ public class PrintJobConfigActivity extends Activity {
private boolean mIgnoreNextCopiesChange;
private boolean mIgnoreNextRangeChange;
private boolean mWaitingForPrinterCapabilities;
public Editor() {
// Content container
mContentContainer = findViewById(R.id.content_container);
@@ -812,13 +839,40 @@ public class PrintJobConfigActivity extends Activity {
// Copies
mCopiesEditText = (EditText) findViewById(R.id.copies_edittext);
mCopiesEditText.setText(String.valueOf(MIN_COPIES));
mSpooler.setPrintJobCopiesNoPersistence(mPrintJobId, MIN_COPIES);
PrintSpooler.peekInstance().setPrintJobCopiesNoPersistence(mPrintJobId, MIN_COPIES);
mCopiesEditText.addTextChangedListener(mCopiesTextWatcher);
mCopiesEditText.selectAll();
// Destination.
mDestinationSpinnerAdapter = new DestinationAdapter(mAvailablePrinters);
mDestinationSpinnerAdapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
// Maybe we did not have capabilities when the current printer was
// selected, but now the selected printer has capabilities. Generate
// a fake selection so the code in the selection change handling takes
// care of updating everything. This way the logic is in one place.
if (mWaitingForPrinterCapabilities) {
mWaitingForPrinterCapabilities = false;
PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
if (printer != null) {
if (printer.getCapabilities() != null) {
final int selectedPosition =
mDestinationSpinner.getSelectedItemPosition();
mOnItemSelectedListener.onItemSelected(mDestinationSpinner, null,
selectedPosition, selectedPosition);
}
}
}
updateUi();
}
@Override
public void onInvalidated() {
updateUi();
}
});
mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner);
mDestinationSpinnerAdapter = new DestinationAdapter();
mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter);
mDestinationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
@@ -877,7 +931,6 @@ public class PrintJobConfigActivity extends Activity {
@Override
public void onClick(View v) {
mEditor.confirmPrint();
updateUi();
mController.update();
showGeneratingPrintJobUi();
}
@@ -1001,6 +1054,11 @@ public class PrintJobConfigActivity extends Activity {
public void confirmPrint() {
mEditorState = EDITOR_STATE_CONFIRMED_PRINT;
PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
if (printer != null) {
mFavoritePrinters.addPrinter(printer);
}
updateUi();
}
public boolean isPreviewConfirmed() {
@@ -1063,8 +1121,8 @@ public class PrintJobConfigActivity extends Activity {
final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
if (selectedIndex < 0 || mDestinationSpinnerAdapter.getItem(
selectedIndex).value.getCapabilities() == null) {
if (selectedIndex < 0 || ((PrinterInfo) mDestinationSpinnerAdapter.getItem(
selectedIndex)).getCapabilities() == null) {
// Destination
mDestinationSpinner.setEnabled(false);
@@ -1121,16 +1179,12 @@ public class PrintJobConfigActivity extends Activity {
mPrintButton.setEnabled(false);
} else {
PrintAttributes defaultAttributes = mTempPrintAttributes;
PrinterInfo printer = mDestinationSpinnerAdapter.getItem(selectedIndex).value;
PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
PrinterCapabilitiesInfo capabilities = printer.getCapabilities();
printer.getCapabilities().getDefaults(defaultAttributes);
// Destination
if (mDestinationSpinnerAdapter.getCount() > 1) {
mDestinationSpinner.setEnabled(true);
} else {
mDestinationSpinner.setEnabled(false);
}
mDestinationSpinner.setEnabled(true);
// Copies
mCopiesEditText.setEnabled(true);
@@ -1159,9 +1213,6 @@ public class PrintJobConfigActivity extends Activity {
if (mediaSizeCount <= 0) {
mMediaSizeSpinner.setEnabled(false);
mMediaSizeSpinner.setSelection(AdapterView.INVALID_POSITION);
} else if (mediaSizeCount == 1) {
mMediaSizeSpinner.setEnabled(false);
mMediaSizeSpinner.setSelection(0);
} else {
mMediaSizeSpinner.setEnabled(true);
final int selectedMediaSizeIndex = Math.max(mediaSizes.indexOf(
@@ -1210,9 +1261,6 @@ public class PrintJobConfigActivity extends Activity {
if (colorModeCount <= 0) {
mColorModeSpinner.setEnabled(false);
mColorModeSpinner.setSelection(AdapterView.INVALID_POSITION);
} else if (colorModeCount == 1) {
mColorModeSpinner.setEnabled(false);
mColorModeSpinner.setSelection(0);
} else {
mColorModeSpinner.setEnabled(true);
final int selectedColorModeIndex = Integer.numberOfTrailingZeros(
@@ -1262,9 +1310,6 @@ public class PrintJobConfigActivity extends Activity {
if (orientationCount <= 0) {
mOrientationSpinner.setEnabled(false);
mOrientationSpinner.setSelection(AdapterView.INVALID_POSITION);
} else if (orientationCount == 1) {
mOrientationSpinner.setEnabled(false);
mOrientationSpinner.setSelection(0);
} else {
mOrientationSpinner.setEnabled(true);
final int selectedOrientationIndex = Integer.numberOfTrailingZeros(
@@ -1336,99 +1381,6 @@ public class PrintJobConfigActivity extends Activity {
}
}
public 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.getName()));
} else {
Log.w(LOG_TAG, "Skipping a duplicate printer: " + addedPrinter);
}
}
if (mDestinationSpinner.getSelectedItemPosition() == AdapterView.INVALID_POSITION
&& mDestinationSpinnerAdapter.getCount() > 0) {
mDestinationSpinner.setSelection(0);
}
mEditor.updateUi();
}
public 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);
}
}
if (mDestinationSpinner.getSelectedItemPosition() != AdapterView.INVALID_POSITION
&& mDestinationSpinnerAdapter.getCount() == 0) {
mDestinationSpinner.setSelection(AdapterView.INVALID_POSITION);
}
}
@SuppressWarnings("unchecked")
public void updatePrinters(List<PrinterInfo> pritners) {
SpinnerItem<PrinterInfo> selectedItem =
(SpinnerItem<PrinterInfo>) mDestinationSpinner.getSelectedItem();
PrinterId selectedPrinterId = (selectedItem != null)
? selectedItem.value.getId() : null;
boolean updated = false;
final int printerCount = pritners.size();
for (int i = 0; i < printerCount; i++) {
PrinterInfo updatedPrinter = pritners.get(i);
final int existingPrinterCount = mDestinationSpinnerAdapter.getCount();
for (int j = 0; j < existingPrinterCount; j++) {
PrinterInfo existingPrinter = mDestinationSpinnerAdapter.getItem(j).value;
if (updatedPrinter.getId().equals(existingPrinter.getId())) {
existingPrinter.copyFrom(updatedPrinter);
updated = true;
if (selectedPrinterId != null
&& selectedPrinterId.equals(updatedPrinter.getId())) {
// The selected printer was updated. We simulate a fake
// selection to reuse the normal printer change handling.
mOnItemSelectedListener.onItemSelected(mDestinationSpinner,
mDestinationSpinner.getSelectedView(),
mDestinationSpinner.getSelectedItemPosition(),
mDestinationSpinner.getSelectedItemId());
// TODO: This will reset the UI to the defaults for the
// printer. We may need to revisit this.
}
break;
}
}
}
if (updated) {
mDestinationSpinnerAdapter.notifyDataSetChanged();
}
}
private boolean hasErrors() {
return mRangeEditText.getError() != null
|| mCopiesEditText.getError() != null;
@@ -1455,10 +1407,39 @@ public class PrintJobConfigActivity extends Activity {
}
}
private final class DestinationAdapter extends ArrayAdapter<SpinnerItem<PrinterInfo>> {
private final class DestinationAdapter extends BaseAdapter {
private final AvailablePrinterProvider mProvider;
public DestinationAdapter() {
super( PrintJobConfigActivity.this, R.layout.spinner_dropdown_item);
private final DataSetObserver mObserver = new DataSetObserver() {
@Override
public void onChanged() {
notifyDataSetChanged();
}
@Override
public void onInvalidated() {
notifyDataSetInvalidated();
}
};
public DestinationAdapter(AvailablePrinterProvider provider) {
mProvider = provider;
mProvider.registerObserver(mObserver);
}
@Override
public int getCount() {
return mProvider.getItemCount();
}
@Override
public Object getItem(int position) {
return mProvider.getItemAt(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
@@ -1474,7 +1455,7 @@ public class PrintJobConfigActivity extends Activity {
R.layout.spinner_dropdown_item, parent, false);
}
PrinterInfo printerInfo = getItem(position).value;
PrinterInfo printerInfo = mProvider.getItemAt(position);
TextView title = (TextView) convertView.findViewById(R.id.title);
title.setText(printerInfo.getName());
@@ -1495,132 +1476,6 @@ public class PrintJobConfigActivity extends Activity {
}
}
private static final class PrinterDiscoverySessionObserver
extends IPrinterDiscoverySessionObserver.Stub {
private static final int MSG_SET_CONTROLLER = 1;
private static final int MSG_ON_PRINTERS_ADDED = 2;
private static final int MSG_ON_PRINTERS_REMOVED = 3;
private static final int MSG_ON_PRINTERS_UPDATED = 4;
private Handler mHandler;
private Editor mEditor;
private IPrinterDiscoverySessionController mController;
@SuppressWarnings("unchecked")
public PrinterDiscoverySessionObserver(Editor editor, Looper looper) {
mEditor = editor;
mHandler = new Handler(looper, null, true) {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_SET_CONTROLLER: {
mController = (IPrinterDiscoverySessionController) message.obj;
// TODO: This should be cleaned up
List<PrinterId> printerIds = Collections.emptyList();
try {
mController.open(printerIds);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error starting printer discovery");
}
} break;
case MSG_ON_PRINTERS_ADDED: {
List<PrinterInfo> printers = (List<PrinterInfo>) message.obj;
mEditor.addPrinters(printers);
} break;
case MSG_ON_PRINTERS_REMOVED: {
List<PrinterId> printerIds = (List<PrinterId>) message.obj;
mEditor.removePrinters(printerIds);
} break;
case MSG_ON_PRINTERS_UPDATED: {
List<PrinterInfo> printers = (List<PrinterInfo>) message.obj;
mEditor.updatePrinters(printers);
} break;
}
}
};
}
public void open(List<PrinterId> priorityList) {
if (mController != null) {
try {
mController.open(priorityList);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error closing printer discovery session", re);
}
}
}
public void close() {
if (mController != null) {
try {
mController.close();
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error closing printer discovery session", re);
}
}
}
public void requestPrinterUpdate(PrinterId printerId) {
if (mController != null) {
try {
mController.requestPrinterUpdate(printerId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error requestin printer update", re);
}
}
}
@Override
public void setController(IPrinterDiscoverySessionController controller) {
synchronized (this) {
if (mHandler != null) {
mHandler.obtainMessage(MSG_SET_CONTROLLER, controller)
.sendToTarget();
}
}
}
@Override
public void onPrintersAdded(List<PrinterInfo> printers) {
synchronized (this) {
if (mHandler != null) {
mHandler.obtainMessage(MSG_ON_PRINTERS_ADDED, printers)
.sendToTarget();
}
}
}
@Override
public void onPrintersRemoved(List<PrinterId> printers) {
synchronized (this) {
if (mHandler != null) {
mHandler.obtainMessage(MSG_ON_PRINTERS_REMOVED, printers)
.sendToTarget();
}
}
}
@Override
public void onPrintersUpdated(List<PrinterInfo> printers) {
synchronized (this) {
if (mHandler != null) {
mHandler.obtainMessage(MSG_ON_PRINTERS_UPDATED, printers)
.sendToTarget();
}
}
}
public void destroy() {
synchronized (this) {
mHandler = null;
mEditor = null;
}
}
}
/**
* An instance of this class class is intended to be the first focusable
* in a layout to which the system automatically gives focus. It performs

View File

@@ -477,7 +477,7 @@ public class PrintSpooler {
private static final String ATTR_FITTING_MODE = "fittingMode";
private static final String ATTR_ORIENTATION = "orientation";
private static final String ATTR_PRINTER_NAME = "printerName";
private static final String ATTR_LOCAL_ID = "printerName";
private static final String ATTR_SERVICE_NAME = "serviceName";
private static final String ATTR_WIDTH_MILS = "widthMils";
@@ -568,7 +568,7 @@ public class PrintSpooler {
PrinterId printerId = printJob.getPrinterId();
if (printerId != null) {
serializer.startTag(null, TAG_PRINTER_ID);
serializer.attribute(null, ATTR_PRINTER_NAME, printerId.getLocalId());
serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
.flattenToString());
serializer.endTag(null, TAG_PRINTER_ID);
@@ -695,13 +695,7 @@ public class PrintSpooler {
Slog.w(LOG_TAG, "Failed to write state, restoring backup.", e);
mStatePersistFile.failWrite(out);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException ioe) {
/* ignore */
}
}
IoUtils.closeQuietly(out);
}
}
@@ -733,11 +727,7 @@ public class PrintSpooler {
} catch (IndexOutOfBoundsException iobe) {
Slog.w(LOG_TAG, "Failed parsing ", iobe);
} finally {
try {
in.close();
} catch (IOException ioe) {
/* ignore */
}
IoUtils.closeQuietly(in);
}
}
@@ -784,7 +774,7 @@ public class PrintSpooler {
skipEmptyTextTags(parser);
if (accept(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID)) {
String localId = parser.getAttributeValue(null, ATTR_PRINTER_NAME);
String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
null, ATTR_SERVICE_NAME));
printJob.setPrinterId(new PrinterId(service, localId));

View File

@@ -110,6 +110,7 @@ final class RemotePrintService implements DeathRecipient {
}
private void handleBinderDied() {
mPendingCommands.clear();
ensureUnbound();
}