blob: 4d5ccc0e803efbd402415593b303e10b470b559a [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 android.print;
import android.annotation.DrawableRes;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.Parcelable;
import android.service.print.PrinterInfoProto;
import android.text.TextUtils;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* This class represents the description of a printer. Instances of
* this class are created by print services to report to the system
* the printers they manage. The information of this class has two
* major components, printer properties such as name, id, status,
* description and printer capabilities which describe the various
* print modes a printer supports such as media sizes, margins, etc.
* <p>
* Once {@link PrinterInfo.Builder#build() built} the objects are immutable.
* </p>
*/
public final class PrinterInfo implements Parcelable {
/** @hide */
@IntDef(prefix = { "STATUS_" }, value = {
STATUS_IDLE,
STATUS_BUSY,
STATUS_UNAVAILABLE
})
@Retention(RetentionPolicy.SOURCE)
public @interface Status {
}
/** Printer status: the printer is idle and ready to print. */
public static final int STATUS_IDLE = PrinterInfoProto.STATUS_IDLE;
/** Printer status: the printer is busy printing. */
public static final int STATUS_BUSY = PrinterInfoProto.STATUS_BUSY;
/** Printer status: the printer is not available. */
public static final int STATUS_UNAVAILABLE = PrinterInfoProto.STATUS_UNAVAILABLE;
private final @NonNull PrinterId mId;
/** Resource inside the printer's services's package to be used as an icon */
private final int mIconResourceId;
/** If a custom icon can be loaded for the printer */
private final boolean mHasCustomPrinterIcon;
/** The generation of the icon in the cache. */
private final int mCustomPrinterIconGen;
/** Intent that launches the activity showing more information about the printer. */
private final @Nullable PendingIntent mInfoIntent;
private final @NonNull String mName;
private final @Status int mStatus;
private final @Nullable String mDescription;
private final @Nullable PrinterCapabilitiesInfo mCapabilities;
private PrinterInfo(@NonNull PrinterId printerId, @NonNull String name, @Status int status,
int iconResourceId, boolean hasCustomPrinterIcon, String description,
PendingIntent infoIntent, PrinterCapabilitiesInfo capabilities,
int customPrinterIconGen) {
mId = printerId;
mName = name;
mStatus = status;
mIconResourceId = iconResourceId;
mHasCustomPrinterIcon = hasCustomPrinterIcon;
mDescription = description;
mInfoIntent = infoIntent;
mCapabilities = capabilities;
mCustomPrinterIconGen = customPrinterIconGen;
}
/**
* Get the globally unique printer id.
*
* @return The printer id.
*/
public @NonNull PrinterId getId() {
return mId;
}
/**
* Get the icon to be used for this printer. If no per printer icon is available, the printer's
* service's icon is returned. If the printer has a custom icon this icon might get requested
* asynchronously. Once the icon is loaded the discovery sessions will be notified that the
* printer changed.
*
* @param context The context that will be using the icons
* @return The icon to be used for the printer or null if no icon could be found.
* @hide
*/
@TestApi
public @Nullable Drawable loadIcon(@NonNull Context context) {
Drawable drawable = null;
PackageManager packageManager = context.getPackageManager();
if (mHasCustomPrinterIcon) {
PrintManager printManager = (PrintManager) context
.getSystemService(Context.PRINT_SERVICE);
Icon icon = printManager.getCustomPrinterIcon(mId);
if (icon != null) {
drawable = icon.loadDrawable(context);
}
}
if (drawable == null) {
try {
String packageName = mId.getServiceName().getPackageName();
PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
ApplicationInfo appInfo = packageInfo.applicationInfo;
// If no custom icon is available, try the icon from the resources
if (mIconResourceId != 0) {
drawable = packageManager.getDrawable(packageName, mIconResourceId, appInfo);
}
// Fall back to the printer's service's icon if no per printer icon could be found
if (drawable == null) {
drawable = appInfo.loadIcon(packageManager);
}
} catch (NameNotFoundException e) {
}
}
return drawable;
}
/**
* Check if the printer has a custom printer icon.
*
* @return {@code true} iff the printer has a custom printer icon.
*
* @hide
*/
public boolean getHasCustomPrinterIcon() {
return mHasCustomPrinterIcon;
}
/**
* Get the printer name.
*
* @return The printer name.
*/
public @NonNull String getName() {
return mName;
}
/**
* Gets the printer status.
*
* @return The status.
*
* @see #STATUS_BUSY
* @see #STATUS_IDLE
* @see #STATUS_UNAVAILABLE
*/
public @Status int getStatus() {
return mStatus;
}
/**
* Gets the printer description.
*
* @return The description.
*/
public @Nullable String getDescription() {
return mDescription;
}
/**
* Get the {@link PendingIntent} that launches the activity showing more information about the
* printer.
*
* @return the {@link PendingIntent} that launches the activity showing more information about
* the printer or null if it is not configured
* @hide
*/
public @Nullable PendingIntent getInfoIntent() {
return mInfoIntent;
}
/**
* Gets the printer capabilities.
*
* @return The capabilities.
*/
public @Nullable PrinterCapabilitiesInfo getCapabilities() {
return mCapabilities;
}
/**
* Check if printerId is valid.
*
* @param printerId The printerId that might be valid
* @return The valid printerId
* @throws IllegalArgumentException if printerId is not valid.
*/
private static @NonNull PrinterId checkPrinterId(PrinterId printerId) {
return Preconditions.checkNotNull(printerId, "printerId cannot be null.");
}
/**
* Check if status is valid.
*
* @param status The status that might be valid
* @return The valid status
* @throws IllegalArgumentException if status is not valid.
*/
private static @Status int checkStatus(int status) {
if (!(status == STATUS_IDLE
|| status == STATUS_BUSY
|| status == STATUS_UNAVAILABLE)) {
throw new IllegalArgumentException("status is invalid.");
}
return status;
}
/**
* Check if name is valid.
*
* @param name The name that might be valid
* @return The valid name
* @throws IllegalArgumentException if name is not valid.
*/
private static @NonNull String checkName(String name) {
return Preconditions.checkStringNotEmpty(name, "name cannot be empty.");
}
private PrinterInfo(Parcel parcel) {
// mName can be null due to unchecked set in Builder.setName and status can be invalid
// due to unchecked set in Builder.setStatus, hence we can only check mId for a valid state
mId = checkPrinterId((PrinterId) parcel.readParcelable(null));
mName = checkName(parcel.readString());
mStatus = checkStatus(parcel.readInt());
mDescription = parcel.readString();
mCapabilities = parcel.readParcelable(null);
mIconResourceId = parcel.readInt();
mHasCustomPrinterIcon = parcel.readByte() != 0;
mCustomPrinterIconGen = parcel.readInt();
mInfoIntent = parcel.readParcelable(null);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeParcelable(mId, flags);
parcel.writeString(mName);
parcel.writeInt(mStatus);
parcel.writeString(mDescription);
parcel.writeParcelable(mCapabilities, flags);
parcel.writeInt(mIconResourceId);
parcel.writeByte((byte) (mHasCustomPrinterIcon ? 1 : 0));
parcel.writeInt(mCustomPrinterIconGen);
parcel.writeParcelable(mInfoIntent, flags);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + mId.hashCode();
result = prime * result + mName.hashCode();
result = prime * result + mStatus;
result = prime * result + ((mDescription != null) ? mDescription.hashCode() : 0);
result = prime * result + ((mCapabilities != null) ? mCapabilities.hashCode() : 0);
result = prime * result + mIconResourceId;
result = prime * result + (mHasCustomPrinterIcon ? 1 : 0);
result = prime * result + mCustomPrinterIconGen;
result = prime * result + ((mInfoIntent != null) ? mInfoIntent.hashCode() : 0);
return result;
}
/**
* Compare two {@link PrinterInfo printerInfos} in all aspects beside being null and the
* {@link #mStatus}.
*
* @param other the other {@link PrinterInfo}
* @return true iff the infos are equivalent
* @hide
*/
public boolean equalsIgnoringStatus(PrinterInfo other) {
if (!mId.equals(other.mId)) {
return false;
}
if (!mName.equals(other.mName)) {
return false;
}
if (!TextUtils.equals(mDescription, other.mDescription)) {
return false;
}
if (mCapabilities == null) {
if (other.mCapabilities != null) {
return false;
}
} else if (!mCapabilities.equals(other.mCapabilities)) {
return false;
}
if (mIconResourceId != other.mIconResourceId) {
return false;
}
if (mHasCustomPrinterIcon != other.mHasCustomPrinterIcon) {
return false;
}
if (mCustomPrinterIconGen != other.mCustomPrinterIconGen) {
return false;
}
if (mInfoIntent == null) {
if (other.mInfoIntent != null) {
return false;
}
} else if (!mInfoIntent.equals(other.mInfoIntent)) {
return false;
}
return true;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
PrinterInfo other = (PrinterInfo) obj;
if (!equalsIgnoringStatus(other)) {
return false;
}
if (mStatus != other.mStatus) {
return false;
}
return true;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("PrinterInfo{");
builder.append("id=").append(mId);
builder.append(", name=").append(mName);
builder.append(", status=").append(mStatus);
builder.append(", description=").append(mDescription);
builder.append(", capabilities=").append(mCapabilities);
builder.append(", iconResId=").append(mIconResourceId);
builder.append(", hasCustomPrinterIcon=").append(mHasCustomPrinterIcon);
builder.append(", customPrinterIconGen=").append(mCustomPrinterIconGen);
builder.append(", infoIntent=").append(mInfoIntent);
builder.append("\"}");
return builder.toString();
}
/**
* Builder for creating of a {@link PrinterInfo}.
*/
public static final class Builder {
private @NonNull PrinterId mPrinterId;
private @NonNull String mName;
private @Status int mStatus;
private int mIconResourceId;
private boolean mHasCustomPrinterIcon;
private String mDescription;
private PendingIntent mInfoIntent;
private PrinterCapabilitiesInfo mCapabilities;
private int mCustomPrinterIconGen;
/**
* Constructor.
*
* @param printerId The printer id. Cannot be null.
* @param name The printer name. Cannot be empty.
* @param status The printer status. Must be a valid status.
* @throws IllegalArgumentException If the printer id is null, or the
* printer name is empty or the status is not a valid one.
*/
public Builder(@NonNull PrinterId printerId, @NonNull String name, @Status int status) {
mPrinterId = checkPrinterId(printerId);
mName = checkName(name);
mStatus = checkStatus(status);
}
/**
* Constructor.
*
* @param other Other info from which to start building.
*/
public Builder(@NonNull PrinterInfo other) {
mPrinterId = other.mId;
mName = other.mName;
mStatus = other.mStatus;
mIconResourceId = other.mIconResourceId;
mHasCustomPrinterIcon = other.mHasCustomPrinterIcon;
mDescription = other.mDescription;
mInfoIntent = other.mInfoIntent;
mCapabilities = other.mCapabilities;
mCustomPrinterIconGen = other.mCustomPrinterIconGen;
}
/**
* Sets the printer status.
*
* @param status The status.
* @return This builder.
* @see PrinterInfo#STATUS_IDLE
* @see PrinterInfo#STATUS_BUSY
* @see PrinterInfo#STATUS_UNAVAILABLE
*/
public @NonNull Builder setStatus(@Status int status) {
mStatus = checkStatus(status);
return this;
}
/**
* Set a drawable resource as icon for this printer. If no icon is set the printer's
* service's icon is used for the printer.
*
* @param iconResourceId The resource ID of the icon.
* @return This builder.
* @see PrinterInfo.Builder#setHasCustomPrinterIcon
*/
public @NonNull Builder setIconResourceId(@DrawableRes int iconResourceId) {
mIconResourceId = Preconditions.checkArgumentNonnegative(iconResourceId,
"iconResourceId can't be negative");
return this;
}
/**
* Declares that the print service can load a custom per printer's icon. If both
* {@link PrinterInfo.Builder#setIconResourceId} and a custom icon are set the resource icon
* is shown while the custom icon loads but then the custom icon is used. If
* {@link PrinterInfo.Builder#setIconResourceId} is not set the printer's service's icon is
* shown while loading.
* <p>
* The icon is requested asynchronously and only when needed via
* {@link android.printservice.PrinterDiscoverySession#onRequestCustomPrinterIcon}.
* </p>
*
* @param hasCustomPrinterIcon If the printer has a custom icon or not.
*
* @return This builder.
*/
public @NonNull Builder setHasCustomPrinterIcon(boolean hasCustomPrinterIcon) {
mHasCustomPrinterIcon = hasCustomPrinterIcon;
return this;
}
/**
* Sets the <strong>localized</strong> printer name which
* is shown to the user
*
* @param name The name.
* @return This builder.
*/
public @NonNull Builder setName(@NonNull String name) {
mName = checkName(name);
return this;
}
/**
* Sets the <strong>localized</strong> printer description
* which is shown to the user
*
* @param description The description.
* @return This builder.
*/
public @NonNull Builder setDescription(@NonNull String description) {
mDescription = description;
return this;
}
/**
* Sets the {@link PendingIntent} that launches an activity showing more information about
* the printer.
*
* @param infoIntent The {@link PendingIntent intent}.
* @return This builder.
*/
public @NonNull Builder setInfoIntent(@NonNull PendingIntent infoIntent) {
mInfoIntent = infoIntent;
return this;
}
/**
* Sets the printer capabilities.
*
* @param capabilities The capabilities.
* @return This builder.
*/
public @NonNull Builder setCapabilities(@NonNull PrinterCapabilitiesInfo capabilities) {
mCapabilities = capabilities;
return this;
}
/**
* Creates a new {@link PrinterInfo}.
*
* @return A new {@link PrinterInfo}.
*/
public @NonNull PrinterInfo build() {
return new PrinterInfo(mPrinterId, mName, mStatus, mIconResourceId,
mHasCustomPrinterIcon, mDescription, mInfoIntent, mCapabilities,
mCustomPrinterIconGen);
}
/**
* Increments the generation number of the custom printer icon. As the {@link PrinterInfo}
* does not match the previous one anymore, users of the {@link PrinterInfo} will reload the
* icon if needed.
*
* @return This builder.
* @hide
*/
public @NonNull Builder incCustomPrinterIconGen() {
mCustomPrinterIconGen++;
return this;
}
}
public static final @android.annotation.NonNull Parcelable.Creator<PrinterInfo> CREATOR =
new Parcelable.Creator<PrinterInfo>() {
@Override
public PrinterInfo createFromParcel(Parcel parcel) {
return new PrinterInfo(parcel);
}
@Override
public PrinterInfo[] newArray(int size) {
return new PrinterInfo[size];
}
};
}