summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSvetoslav <svetoslavganov@google.com>2015-02-02 19:47:04 -0800
committerSvetoslav Ganov <svetoslavganov@google.com>2015-02-04 05:48:48 +0000
commit948c9a6393e0995a4d988d5d164998aa0e12b25d (patch)
tree8ff11da699702a25b697074c2ce7e181cb2593b6
parentd7596cff0b61a58e089f76bee3c626486bcbaec7 (diff)
downloadframeworks_base-948c9a6393e0995a4d988d5d164998aa0e12b25d.zip
frameworks_base-948c9a6393e0995a4d988d5d164998aa0e12b25d.tar.gz
frameworks_base-948c9a6393e0995a4d988d5d164998aa0e12b25d.tar.bz2
Add duplex mode support.
This change adds support for duplex printing. The print UI now has a duplex option which allows the user to choose one of the supported duplex options by the currently selected printer. The chosen duplex mode is propaged to the print service that manages this printer. Change-Id: I807ba9da2723531535c0e1e33f2f4e1b503a54b0
-rw-r--r--api/current.txt7
-rw-r--r--api/system-current.txt7
-rw-r--r--core/java/android/print/PrintAttributes.java109
-rw-r--r--core/java/android/print/PrinterCapabilitiesInfo.java91
-rw-r--r--packages/PrintSpooler/res/layout/print_activity_controls.xml28
-rw-r--r--packages/PrintSpooler/res/values/strings.xml15
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java11
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java108
8 files changed, 359 insertions, 17 deletions
diff --git a/api/current.txt b/api/current.txt
index 123d8cc..0c77904 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -23234,6 +23234,7 @@ package android.print {
public final class PrintAttributes implements android.os.Parcelable {
method public int describeContents();
method public int getColorMode();
+ method public int getDuplexMode();
method public android.print.PrintAttributes.MediaSize getMediaSize();
method public android.print.PrintAttributes.Margins getMinMargins();
method public android.print.PrintAttributes.Resolution getResolution();
@@ -23241,12 +23242,16 @@ package android.print {
field public static final int COLOR_MODE_COLOR = 2; // 0x2
field public static final int COLOR_MODE_MONOCHROME = 1; // 0x1
field public static final android.os.Parcelable.Creator<android.print.PrintAttributes> CREATOR;
+ field public static final int DUPLEX_MODE_LONG_EDGE = 2; // 0x2
+ field public static final int DUPLEX_MODE_NONE = 1; // 0x1
+ field public static final int DUPLEX_MODE_SHORT_EDGE = 4; // 0x4
}
public static final class PrintAttributes.Builder {
ctor public PrintAttributes.Builder();
method public android.print.PrintAttributes build();
method public android.print.PrintAttributes.Builder setColorMode(int);
+ method public android.print.PrintAttributes.Builder setDuplexMode(int);
method public android.print.PrintAttributes.Builder setMediaSize(android.print.PrintAttributes.MediaSize);
method public android.print.PrintAttributes.Builder setMinMargins(android.print.PrintAttributes.Margins);
method public android.print.PrintAttributes.Builder setResolution(android.print.PrintAttributes.Resolution);
@@ -23464,6 +23469,7 @@ package android.print {
method public int describeContents();
method public int getColorModes();
method public android.print.PrintAttributes getDefaults();
+ method public int getDuplexModes();
method public java.util.List<android.print.PrintAttributes.MediaSize> getMediaSizes();
method public android.print.PrintAttributes.Margins getMinMargins();
method public java.util.List<android.print.PrintAttributes.Resolution> getResolutions();
@@ -23477,6 +23483,7 @@ package android.print {
method public android.print.PrinterCapabilitiesInfo.Builder addResolution(android.print.PrintAttributes.Resolution, boolean);
method public android.print.PrinterCapabilitiesInfo build();
method public android.print.PrinterCapabilitiesInfo.Builder setColorModes(int, int);
+ method public android.print.PrinterCapabilitiesInfo.Builder setDuplexModes(int, int);
method public android.print.PrinterCapabilitiesInfo.Builder setMinMargins(android.print.PrintAttributes.Margins);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 6224dfb..8597e25 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24823,6 +24823,7 @@ package android.print {
public final class PrintAttributes implements android.os.Parcelable {
method public int describeContents();
method public int getColorMode();
+ method public int getDuplexMode();
method public android.print.PrintAttributes.MediaSize getMediaSize();
method public android.print.PrintAttributes.Margins getMinMargins();
method public android.print.PrintAttributes.Resolution getResolution();
@@ -24830,12 +24831,16 @@ package android.print {
field public static final int COLOR_MODE_COLOR = 2; // 0x2
field public static final int COLOR_MODE_MONOCHROME = 1; // 0x1
field public static final android.os.Parcelable.Creator<android.print.PrintAttributes> CREATOR;
+ field public static final int DUPLEX_MODE_LONG_EDGE = 2; // 0x2
+ field public static final int DUPLEX_MODE_NONE = 1; // 0x1
+ field public static final int DUPLEX_MODE_SHORT_EDGE = 4; // 0x4
}
public static final class PrintAttributes.Builder {
ctor public PrintAttributes.Builder();
method public android.print.PrintAttributes build();
method public android.print.PrintAttributes.Builder setColorMode(int);
+ method public android.print.PrintAttributes.Builder setDuplexMode(int);
method public android.print.PrintAttributes.Builder setMediaSize(android.print.PrintAttributes.MediaSize);
method public android.print.PrintAttributes.Builder setMinMargins(android.print.PrintAttributes.Margins);
method public android.print.PrintAttributes.Builder setResolution(android.print.PrintAttributes.Resolution);
@@ -25053,6 +25058,7 @@ package android.print {
method public int describeContents();
method public int getColorModes();
method public android.print.PrintAttributes getDefaults();
+ method public int getDuplexModes();
method public java.util.List<android.print.PrintAttributes.MediaSize> getMediaSizes();
method public android.print.PrintAttributes.Margins getMinMargins();
method public java.util.List<android.print.PrintAttributes.Resolution> getResolutions();
@@ -25066,6 +25072,7 @@ package android.print {
method public android.print.PrinterCapabilitiesInfo.Builder addResolution(android.print.PrintAttributes.Resolution, boolean);
method public android.print.PrinterCapabilitiesInfo build();
method public android.print.PrinterCapabilitiesInfo.Builder setColorModes(int, int);
+ method public android.print.PrinterCapabilitiesInfo.Builder setDuplexModes(int, int);
method public android.print.PrinterCapabilitiesInfo.Builder setMinMargins(android.print.PrintAttributes.Margins);
}
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index 30f0c6a..90d30d6 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -45,11 +45,22 @@ public final class PrintAttributes implements Parcelable {
private static final int VALID_COLOR_MODES =
COLOR_MODE_MONOCHROME | COLOR_MODE_COLOR;
+ /** Duplex mode: No duplexing. */
+ public static final int DUPLEX_MODE_NONE = 1 << 0;
+ /** Duplex mode: Pages are turned sideways along the long edge - like a book. */
+ public static final int DUPLEX_MODE_LONG_EDGE = 1 << 1;
+ /** Duplex mode: Pages are turned upwards along the short edge - like a notpad. */
+ public static final int DUPLEX_MODE_SHORT_EDGE = 1 << 2;
+
+ private static final int VALID_DUPLEX_MODES =
+ DUPLEX_MODE_NONE | DUPLEX_MODE_LONG_EDGE | DUPLEX_MODE_SHORT_EDGE;
+
private MediaSize mMediaSize;
private Resolution mResolution;
private Margins mMinMargins;
private int mColorMode;
+ private int mDuplexMode = DUPLEX_MODE_NONE;
PrintAttributes() {
/* hide constructor */
@@ -60,6 +71,7 @@ public final class PrintAttributes implements Parcelable {
mResolution = (parcel.readInt() == 1) ? Resolution.createFromParcel(parcel) : null;
mMinMargins = (parcel.readInt() == 1) ? Margins.createFromParcel(parcel) : null;
mColorMode = parcel.readInt();
+ mDuplexMode = parcel.readInt();
}
/**
@@ -74,7 +86,7 @@ public final class PrintAttributes implements Parcelable {
/**
* Sets the media size.
*
- * @param The media size.
+ * @param mediaSize The media size.
*
* @hide
*/
@@ -94,7 +106,7 @@ public final class PrintAttributes implements Parcelable {
/**
* Sets the resolution.
*
- * @param The resolution.
+ * @param resolution The resolution.
*
* @hide
*/
@@ -130,7 +142,7 @@ public final class PrintAttributes implements Parcelable {
* </strong>
* </p>
*
- * @param The margins.
+ * @param margins The margins.
*
* @hide
*/
@@ -153,7 +165,7 @@ public final class PrintAttributes implements Parcelable {
/**
* Sets the color mode.
*
- * @param The color mode.
+ * @param colorMode The color mode.
*
* @see #COLOR_MODE_MONOCHROME
* @see #COLOR_MODE_COLOR
@@ -179,6 +191,35 @@ public final class PrintAttributes implements Parcelable {
}
/**
+ * Gets the duplex mode.
+ *
+ * @return The duplex mode.
+ *
+ * @see #DUPLEX_MODE_NONE
+ * @see #DUPLEX_MODE_LONG_EDGE
+ * @see #DUPLEX_MODE_SHORT_EDGE
+ */
+ public int getDuplexMode() {
+ return mDuplexMode;
+ }
+
+ /**
+ * Sets the duplex mode.
+ *
+ * @param duplexMode The duplex mode.
+ *
+ * @see #DUPLEX_MODE_NONE
+ * @see #DUPLEX_MODE_LONG_EDGE
+ * @see #DUPLEX_MODE_SHORT_EDGE
+ *
+ * @hide
+ */
+ public void setDuplexMode(int duplexMode) {
+ enforceValidDuplexMode(duplexMode);
+ mDuplexMode = duplexMode;
+ }
+
+ /**
* Gets a new print attributes instance which is in portrait orientation,
* which is the media size is in portrait and all orientation dependent
* attributes such as resolution and margins are properly adjusted.
@@ -211,6 +252,7 @@ public final class PrintAttributes implements Parcelable {
attributes.setMinMargins(getMinMargins());
attributes.setColorMode(getColorMode());
+ attributes.setDuplexMode(getDuplexMode());
return attributes;
}
@@ -248,6 +290,7 @@ public final class PrintAttributes implements Parcelable {
attributes.setMinMargins(getMinMargins());
attributes.setColorMode(getColorMode());
+ attributes.setDuplexMode(getDuplexMode());
return attributes;
}
@@ -273,6 +316,7 @@ public final class PrintAttributes implements Parcelable {
parcel.writeInt(0);
}
parcel.writeInt(mColorMode);
+ parcel.writeInt(mDuplexMode);
}
@Override
@@ -285,6 +329,7 @@ public final class PrintAttributes implements Parcelable {
final int prime = 31;
int result = 1;
result = prime * result + mColorMode;
+ result = prime * result + mDuplexMode;
result = prime * result + ((mMinMargins == null) ? 0 : mMinMargins.hashCode());
result = prime * result + ((mMediaSize == null) ? 0 : mMediaSize.hashCode());
result = prime * result + ((mResolution == null) ? 0 : mResolution.hashCode());
@@ -306,6 +351,9 @@ public final class PrintAttributes implements Parcelable {
if (mColorMode != other.mColorMode) {
return false;
}
+ if (mDuplexMode != other.mDuplexMode) {
+ return false;
+ }
if (mMinMargins == null) {
if (other.mMinMargins != null) {
return false;
@@ -344,6 +392,7 @@ public final class PrintAttributes implements Parcelable {
builder.append(", resolution: ").append(mResolution);
builder.append(", minMargins: ").append(mMinMargins);
builder.append(", colorMode: ").append(colorModeToString(mColorMode));
+ builder.append(", duplexMode: ").append(duplexModeToString(mDuplexMode));
builder.append("}");
return builder.toString();
}
@@ -354,6 +403,7 @@ public final class PrintAttributes implements Parcelable {
mResolution = null;
mMinMargins = null;
mColorMode = 0;
+ mDuplexMode = DUPLEX_MODE_NONE;
}
/**
@@ -364,6 +414,7 @@ public final class PrintAttributes implements Parcelable {
mResolution = other.mResolution;
mMinMargins = other.mMinMargins;
mColorMode = other.mColorMode;
+ mDuplexMode = other.mDuplexMode;
}
/**
@@ -1270,17 +1321,41 @@ public final class PrintAttributes implements Parcelable {
case COLOR_MODE_COLOR: {
return "COLOR_MODE_COLOR";
}
- default:
+ default: {
return "COLOR_MODE_UNKNOWN";
+ }
+ }
+ }
+
+ static String duplexModeToString(int duplexMode) {
+ switch (duplexMode) {
+ case DUPLEX_MODE_NONE: {
+ return "DUPLEX_MODE_NONE";
+ }
+ case DUPLEX_MODE_LONG_EDGE: {
+ return "DUPLEX_MODE_LONG_EDGE";
+ }
+ case DUPLEX_MODE_SHORT_EDGE: {
+ return "DUPLEX_MODE_SHORT_EDGE";
+ }
+ default: {
+ return "DUPLEX_MODE_UNKNOWN";
+ }
}
}
static void enforceValidColorMode(int colorMode) {
- if ((colorMode & VALID_COLOR_MODES) == 0 && Integer.bitCount(colorMode) == 1) {
+ if ((colorMode & VALID_COLOR_MODES) == 0 || Integer.bitCount(colorMode) != 1) {
throw new IllegalArgumentException("invalid color mode: " + colorMode);
}
}
+ static void enforceValidDuplexMode(int duplexMode) {
+ if ((duplexMode & VALID_DUPLEX_MODES) == 0 || Integer.bitCount(duplexMode) != 1) {
+ throw new IllegalArgumentException("invalid duplex mode: " + duplexMode);
+ }
+ }
+
/**
* Builder for creating {@link PrintAttributes}.
*/
@@ -1331,15 +1406,31 @@ public final class PrintAttributes implements Parcelable {
* @see PrintAttributes#COLOR_MODE_COLOR
*/
public Builder setColorMode(int colorMode) {
- if (Integer.bitCount(colorMode) > 1) {
- throw new IllegalArgumentException("can specify at most one colorMode bit.");
- }
mAttributes.setColorMode(colorMode);
return this;
}
/**
+ * Sets the duplex mode.
+ *
+ * @param duplexMode A valid duplex mode or zero.
+ * @return This builder.
+ *
+ * @see PrintAttributes#DUPLEX_MODE_NONE
+ * @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
+ * @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
+ */
+ public Builder setDuplexMode(int duplexMode) {
+ mAttributes.setDuplexMode(duplexMode);
+ return this;
+ }
+
+ /**
* Creates a new {@link PrintAttributes} instance.
+ * <p>
+ * If you do not specify a duplex mode, the default
+ * {@link #DUPLEX_MODE_NONE} will be used.
+ * </p>
*
* @return The new instance.
*/
diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java
index 806a89d..96f3185 100644
--- a/core/java/android/print/PrinterCapabilitiesInfo.java
+++ b/core/java/android/print/PrinterCapabilitiesInfo.java
@@ -47,7 +47,8 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
private static final int PROPERTY_MEDIA_SIZE = 0;
private static final int PROPERTY_RESOLUTION = 1;
private static final int PROPERTY_COLOR_MODE = 2;
- private static final int PROPERTY_COUNT = 3;
+ private static final int PROPERTY_DUPLEX_MODE = 3;
+ private static final int PROPERTY_COUNT = 4;
private static final Margins DEFAULT_MARGINS = new Margins(0, 0, 0, 0);
@@ -56,6 +57,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
private List<Resolution> mResolutions;
private int mColorModes;
+ private int mDuplexModes;
private final int[] mDefaults = new int[PROPERTY_COUNT];
@@ -106,6 +108,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
}
mColorModes = other.mColorModes;
+ mDuplexModes = other.mDuplexModes;
final int defaultCount = other.mDefaults.length;
for (int i = 0; i < defaultCount; i++) {
@@ -154,6 +157,19 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
}
/**
+ * Gets the bit mask of supported duplex modes.
+ *
+ * @return The bit mask of supported duplex modes.
+ *
+ * @see PrintAttributes#DUPLEX_MODE_NONE
+ * @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
+ * @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
+ */
+ public int getDuplexModes() {
+ return mDuplexModes;
+ }
+
+ /**
* Gets the default print attributes.
*
* @return The default attributes.
@@ -178,6 +194,11 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
builder.setColorMode(colorMode);
}
+ final int duplexMode = mDefaults[PROPERTY_DUPLEX_MODE];
+ if (duplexMode > 0) {
+ builder.setDuplexMode(duplexMode);
+ }
+
return builder.build();
}
@@ -187,6 +208,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
readResolutions(parcel);
mColorModes = parcel.readInt();
+ mDuplexModes = parcel.readInt();
readDefaults(parcel);
}
@@ -203,6 +225,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
writeResolutions(parcel);
parcel.writeInt(mColorModes);
+ parcel.writeInt(mDuplexModes);
writeDefaults(parcel);
}
@@ -215,6 +238,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
result = prime * result + ((mMediaSizes == null) ? 0 : mMediaSizes.hashCode());
result = prime * result + ((mResolutions == null) ? 0 : mResolutions.hashCode());
result = prime * result + mColorModes;
+ result = prime * result + mDuplexModes;
result = prime * result + Arrays.hashCode(mDefaults);
return result;
}
@@ -255,6 +279,9 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
if (mColorModes != other.mColorModes) {
return false;
}
+ if (mDuplexModes != other.mDuplexModes) {
+ return false;
+ }
if (!Arrays.equals(mDefaults, other.mDefaults)) {
return false;
}
@@ -269,6 +296,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
builder.append(", mediaSizes=").append(mMediaSizes);
builder.append(", resolutions=").append(mResolutions);
builder.append(", colorModes=").append(colorModesToString());
+ builder.append(", duplexModes=").append(duplexModesToString());
builder.append("\"}");
return builder.toString();
}
@@ -289,6 +317,22 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
return builder.toString();
}
+ private String duplexModesToString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append('[');
+ int duplexModes = mDuplexModes;
+ while (duplexModes != 0) {
+ final int duplexMode = 1 << Integer.numberOfTrailingZeros(duplexModes);
+ duplexModes &= ~duplexMode;
+ if (builder.length() > 1) {
+ builder.append(", ");
+ }
+ builder.append(PrintAttributes.duplexModeToString(duplexMode));
+ }
+ builder.append(']');
+ return builder.toString();
+ }
+
private void writeMediaSizes(Parcel parcel) {
if (mMediaSizes == null) {
parcel.writeInt(0);
@@ -495,19 +539,51 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
currentModes &= ~currentMode;
PrintAttributes.enforceValidColorMode(currentMode);
}
- if ((colorModes & defaultColorMode) == 0) {
- throw new IllegalArgumentException("Default color mode not in color modes.");
- }
- PrintAttributes.enforceValidColorMode(colorModes);
+ PrintAttributes.enforceValidColorMode(defaultColorMode);
mPrototype.mColorModes = colorModes;
mPrototype.mDefaults[PROPERTY_COLOR_MODE] = defaultColorMode;
return this;
}
/**
+ * Sets the duplex modes.
+ * <p>
+ * <strong>Required:</strong> No
+ * </p>
+ *
+ * @param duplexModes The duplex mode bit mask.
+ * @param defaultDuplexMode The default duplex mode.
+ * @return This builder.
+ *
+ * @throws IllegalArgumentException If duplex modes contains an invalid
+ * mode bit or if the default duplex mode is invalid.
+ *
+ * @see PrintAttributes#DUPLEX_MODE_NONE
+ * @see PrintAttributes#DUPLEX_MODE_LONG_EDGE
+ * @see PrintAttributes#DUPLEX_MODE_SHORT_EDGE
+ */
+ public Builder setDuplexModes(int duplexModes, int defaultDuplexMode) {
+ int currentModes = duplexModes;
+ while (currentModes > 0) {
+ final int currentMode = (1 << Integer.numberOfTrailingZeros(currentModes));
+ currentModes &= ~currentMode;
+ PrintAttributes.enforceValidDuplexMode(currentMode);
+ }
+ PrintAttributes.enforceValidDuplexMode(defaultDuplexMode);
+ mPrototype.mDuplexModes = duplexModes;
+ mPrototype.mDefaults[PROPERTY_DUPLEX_MODE] = defaultDuplexMode;
+ return this;
+ }
+
+ /**
* Crates a new {@link PrinterCapabilitiesInfo} enforcing that all
* required properties have been specified. See individual methods
* in this class for reference about required attributes.
+ * <p>
+ * <strong>Note:</strong> If you do not add supported duplex modes,
+ * {@link android.print.PrintAttributes#DUPLEX_MODE_NONE} will set
+ * as the only supported mode and also as the default duplex mode.
+ * </p>
*
* @return A new {@link PrinterCapabilitiesInfo}.
*
@@ -532,6 +608,10 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
if (mPrototype.mDefaults[PROPERTY_COLOR_MODE] == DEFAULT_UNDEFINED) {
throw new IllegalStateException("No default color mode specified.");
}
+ if (mPrototype.mDuplexModes == 0) {
+ setDuplexModes(PrintAttributes.DUPLEX_MODE_NONE,
+ PrintAttributes.DUPLEX_MODE_NONE);
+ }
if (mPrototype.mMinMargins == null) {
throw new IllegalArgumentException("margins cannot be null");
}
@@ -558,4 +638,3 @@ public final class PrinterCapabilitiesInfo implements Parcelable {
}
};
}
-
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml
index 0bf64aa..a87afe0 100644
--- a/packages/PrintSpooler/res/layout/print_activity_controls.xml
+++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml
@@ -159,6 +159,34 @@
android:layout_marginEnd="16dip"
android:orientation="vertical">
+ <!-- Duplex -->
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="12dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:labelFor="@+id/duplex_spinner"
+ android:text="@string/label_duplex">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/duplex_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dip">
+ </Spinner>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip"
+ android:layout_marginEnd="16dip"
+ android:orientation="vertical">
+
<!-- Range options -->
<TextView
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index ab633ea..9d67ccc 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -40,6 +40,9 @@
<!-- Label of the color mode widget. [CHAR LIMIT=20] -->
<string name="label_color">Color</string>
+ <!-- Label of the duplex mode widget. [CHAR LIMIT=20] -->
+ <string name="label_duplex">Duplex</string>
+
<!-- Label of the orientation widget. [CHAR LIMIT=20] -->
<string name="label_orientation">Orientation</string>
@@ -188,12 +191,22 @@
<!-- Color mode labels. -->
<string-array name="color_mode_labels">
- <!-- Color modelabel: Monochrome color scheme, e.g. one color is used. [CHAR LIMIT=20] -->
+ <!-- Color mode label: Monochrome color scheme, e.g. one color is used. [CHAR LIMIT=20] -->
<item>Black &amp; White</item>
<!-- Color mode label: Color color scheme, e.g. many colors are used. [CHAR LIMIT=20] -->
<item>Color</item>
</string-array>
+ <!-- Duplex mode labels. -->
+ <string-array name="duplex_mode_labels">
+ <!-- Duplex mode label: No duplex supported. [CHAR LIMIT=20] -->
+ <item>None</item>
+ <!-- Duplex mode label: Turn page sideways along the long edge like a book. [CHAR LIMIT=20] -->
+ <item>Long edge</item>
+ <!-- Duplex mode label: Turn page upwards along the short edge like a notepad. [CHAR LIMIT=20] -->
+ <item>Short edge</item>
+ </string-array>
+
<!-- Orientation labels. -->
<string-array name="orientation_labels">
<!-- Orientation label: Portrait page orientation. [CHAR LIMIT=30] -->
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index 2cc5e04..377d2d5 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -695,6 +695,7 @@ public final class PrintSpoolerService extends Service {
private static final String TAG_MARGINS = "margins";
private static final String ATTR_COLOR_MODE = "colorMode";
+ private static final String ATTR_DUPLEX_MODE = "duplexMode";
private static final String ATTR_LOCAL_ID = "localId";
private static final String ATTR_SERVICE_NAME = "serviceName";
@@ -823,6 +824,10 @@ public final class PrintSpoolerService extends Service {
serializer.attribute(null, ATTR_COLOR_MODE,
String.valueOf(colorMode));
+ final int duplexMode = attributes.getDuplexMode();
+ serializer.attribute(null, ATTR_DUPLEX_MODE,
+ String.valueOf(duplexMode));
+
MediaSize mediaSize = attributes.getMediaSize();
if (mediaSize != null) {
serializer.startTag(null, TAG_MEDIA_SIZE);
@@ -1057,6 +1062,12 @@ public final class PrintSpoolerService extends Service {
String colorMode = parser.getAttributeValue(null, ATTR_COLOR_MODE);
builder.setColorMode(Integer.parseInt(colorMode));
+ String duplexMode = parser.getAttributeValue(null, ATTR_DUPLEX_MODE);
+ // Duplex mode was added later, so null check is needed.
+ if (duplexMode != null) {
+ builder.setDuplexMode(Integer.parseInt(duplexMode));
+ }
+
parser.next();
skipEmptyTextTags(parser);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index f3a5c95..4ba04e5 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -184,6 +184,9 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
private Spinner mColorModeSpinner;
private ArrayAdapter<SpinnerItem<Integer>> mColorModeSpinnerAdapter;
+ private Spinner mDuplexModeSpinner;
+ private ArrayAdapter<SpinnerItem<Integer>> mDuplexModeSpinnerAdapter;
+
private Spinner mOrientationSpinner;
private ArrayAdapter<SpinnerItem<Integer>> mOrientationSpinnerAdapter;
@@ -767,6 +770,21 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
}
}
}
+
+ // Take the duplex mode only if the current printer supports it.
+ final int currDuplexMode = currAttributes.getDuplexMode();
+ final int newDuplexMode = newAttributes.getDuplexMode();
+ if (currDuplexMode != newDuplexMode) {
+ final int duplexModeCount = mDuplexModeSpinner.getCount();
+ for (int i = 0; i < duplexModeCount; i++) {
+ final int supportedDuplexMode = mDuplexModeSpinnerAdapter.getItem(i).value;
+ if (supportedDuplexMode == newDuplexMode) {
+ currAttributes.setDuplexMode(newDuplexMode);
+ mDuplexModeSpinner.setSelection(i);
+ break;
+ }
+ }
+ }
}
// Handle selected page changes making sure they are in the doc.
@@ -985,6 +1003,12 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
attributes.setColorMode(defaults.getColorMode());
}
+ // Duplex mode.
+ final int duplexMode = attributes.getDuplexMode();
+ if ((capabilities.getDuplexModes() & duplexMode) == 0) {
+ attributes.setDuplexMode(defaults.getDuplexMode());
+ }
+
// Resolution
Resolution resolution = attributes.getResolution();
if (resolution == null || !capabilities.getResolutions().contains(resolution)) {
@@ -1111,6 +1135,13 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
mColorModeSpinner.setOnItemSelectedListener(itemSelectedListener);
+ // Duplex mode.
+ mDuplexModeSpinnerAdapter = new ArrayAdapter<>(
+ this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
+ mDuplexModeSpinner = (Spinner) findViewById(R.id.duplex_spinner);
+ mDuplexModeSpinner.setAdapter(mDuplexModeSpinnerAdapter);
+ mDuplexModeSpinner.setOnItemSelectedListener(itemSelectedListener);
+
// Orientation
mOrientationSpinnerAdapter = new ArrayAdapter<>(
this, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1);
@@ -1187,6 +1218,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
mCopiesEditText.setFocusable(false);
mMediaSizeSpinner.setEnabled(false);
mColorModeSpinner.setEnabled(false);
+ mDuplexModeSpinner.setEnabled(false);
mOrientationSpinner.setEnabled(false);
mRangeOptionsSpinner.setEnabled(false);
mPageRangeEditText.setEnabled(false);
@@ -1202,6 +1234,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
mCopiesEditText.setFocusable(false);
mMediaSizeSpinner.setEnabled(false);
mColorModeSpinner.setEnabled(false);
+ mDuplexModeSpinner.setEnabled(false);
mOrientationSpinner.setEnabled(false);
mRangeOptionsSpinner.setEnabled(false);
mPageRangeEditText.setEnabled(false);
@@ -1317,7 +1350,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
final int colorMode = 1 << colorBitOffset;
if (colorMode == oldColorMode) {
// Update the index of the old selection.
- oldColorModeNewIndex = colorBitOffset;
+ oldColorModeNewIndex = mColorModeSpinnerAdapter.getCount();
}
remainingColorModes &= ~colorMode;
mColorModeSpinnerAdapter.add(new SpinnerItem<>(colorMode,
@@ -1339,11 +1372,81 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
mColorModeSpinner.setSelection(i);
}
attributes.setColorMode(selectedColorMode);
+ break;
}
}
}
}
+ // Duplex mode.
+ mDuplexModeSpinner.setEnabled(true);
+ final int duplexModes = capabilities.getDuplexModes();
+
+ // If the duplex modes changed, we update the adapter and the spinner.
+ // Note that we use bit count +1 to account for the no duplex option.
+ boolean duplexModesChanged = false;
+ if (Integer.bitCount(duplexModes) != mDuplexModeSpinnerAdapter.getCount()) {
+ duplexModesChanged = true;
+ } else {
+ int remainingDuplexModes = duplexModes;
+ int adapterIndex = 0;
+ while (remainingDuplexModes != 0) {
+ final int duplexBitOffset = Integer.numberOfTrailingZeros(remainingDuplexModes);
+ final int duplexMode = 1 << duplexBitOffset;
+ remainingDuplexModes &= ~duplexMode;
+ if (duplexMode != mDuplexModeSpinnerAdapter.getItem(adapterIndex).value) {
+ duplexModesChanged = true;
+ break;
+ }
+ adapterIndex++;
+ }
+ }
+ if (duplexModesChanged) {
+ // Remember the old duplex mode to try selecting it again. Also the fallback
+ // is no duplexing which is always the first item in the dropdown.
+ int oldDuplexModeNewIndex = AdapterView.INVALID_POSITION;
+ final int oldDuplexMode = attributes.getDuplexMode();
+
+ // Rebuild the adapter data.
+ 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;
+ if (duplexMode == oldDuplexMode) {
+ // Update the index of the old selection.
+ oldDuplexModeNewIndex = mDuplexModeSpinnerAdapter.getCount();
+ }
+ remainingDuplexModes &= ~duplexMode;
+ mDuplexModeSpinnerAdapter.add(new SpinnerItem<>(duplexMode,
+ duplexModeLabels[duplexBitOffset]));
+ }
+
+ if (oldDuplexModeNewIndex != AdapterView.INVALID_POSITION) {
+ // Select the old duplex mode - nothing really changed.
+ if (mDuplexModeSpinner.getSelectedItemPosition() != oldDuplexModeNewIndex) {
+ mDuplexModeSpinner.setSelection(oldDuplexModeNewIndex);
+ }
+ } else {
+ // Select the default.
+ final int selectedDuplexMode = defaultAttributes.getDuplexMode();
+ final int itemCount = mDuplexModeSpinnerAdapter.getCount();
+ for (int i = 0; i < itemCount; i++) {
+ SpinnerItem<Integer> item = mDuplexModeSpinnerAdapter.getItem(i);
+ if (selectedDuplexMode == item.value) {
+ if (mDuplexModeSpinner.getSelectedItemPosition() != i) {
+ mDuplexModeSpinner.setSelection(i);
+ }
+ attributes.setDuplexMode(selectedDuplexMode);
+ break;
+ }
+ }
+ }
+ }
+
+ mDuplexModeSpinner.setEnabled(mDuplexModeSpinnerAdapter.getCount() > 1);
+
// Orientation
mOrientationSpinner.setEnabled(true);
MediaSize mediaSize = attributes.getMediaSize();
@@ -2173,6 +2276,9 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
} else if (spinner == mColorModeSpinner) {
SpinnerItem<Integer> colorModeItem = mColorModeSpinnerAdapter.getItem(position);
mPrintJob.getAttributes().setColorMode(colorModeItem.value);
+ } else if (spinner == mDuplexModeSpinner) {
+ SpinnerItem<Integer> duplexModeItem = mDuplexModeSpinnerAdapter.getItem(position);
+ mPrintJob.getAttributes().setDuplexMode(duplexModeItem.value);
} else if (spinner == mOrientationSpinner) {
SpinnerItem<Integer> orientationItem = mOrientationSpinnerAdapter.getItem(position);
PrintAttributes attributes = mPrintJob.getAttributes();