1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.print;
18
19import android.annotation.IntDef;
20import android.annotation.IntRange;
21import android.annotation.NonNull;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.text.TextUtils;
25import com.android.internal.util.Preconditions;
26
27import java.lang.annotation.Retention;
28import java.lang.annotation.RetentionPolicy;
29
30/**
31 * This class encapsulates information about a document for printing
32 * purposes. This meta-data is used by the platform and print services,
33 * components that interact with printers. For example, this class
34 * contains the number of pages contained in the document it describes and
35 * this number of pages is shown to the user allowing him/her to select
36 * the range to print. Also a print service may optimize the printing
37 * process based on the content type, such as document or photo.
38 * <p>
39 * Instances of this class are created by the printing application and
40 * passed to the {@link PrintDocumentAdapter.LayoutResultCallback#onLayoutFinished(
41 * PrintDocumentInfo, boolean) PrintDocumentAdapter.LayoutResultCallback.onLayoutFinished(
42 * PrintDocumentInfo, boolean)} callback after successfully laying out the
43 * content which is performed in {@link PrintDocumentAdapter#onLayout(PrintAttributes,
44 * PrintAttributes, android.os.CancellationSignal, PrintDocumentAdapter.LayoutResultCallback,
45 * android.os.Bundle) PrintDocumentAdapter.onLayout(PrintAttributes,
46 * PrintAttributes, android.os.CancellationSignal,
47 * PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle)}.
48 * </p>
49 * <p>
50 * An example usage looks like this:
51 * <pre>
52 *
53 * . . .
54 *
55 * public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
56 *         CancellationSignal cancellationSignal, LayoutResultCallback callback,
57 *         Bundle metadata) {
58 *
59 *        // Assume the app defined a LayoutResult class which contains
60 *        // the layout result data and that the content is a document.
61 *        LayoutResult result = doSomeLayoutWork();
62 *
63 *        PrintDocumentInfo info = new PrintDocumentInfo
64 *                .Builder("printed_file.pdf")
65 *                .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
66 *                .setPageCount(result.getPageCount())
67 *                .build();
68 *
69 *       callback.onLayoutFinished(info, result.getContentChanged());
70 *   }
71 *
72 *   . . .
73 *
74 * </pre>
75 * </p>
76 */
77public final class PrintDocumentInfo implements Parcelable {
78
79    /**
80     * Constant for unknown page count.
81     */
82    public static final int PAGE_COUNT_UNKNOWN = -1;
83
84    /** @hide */
85    @Retention(RetentionPolicy.SOURCE)
86    @IntDef({
87            CONTENT_TYPE_UNKNOWN, CONTENT_TYPE_DOCUMENT, CONTENT_TYPE_PHOTO
88    })
89    public @interface ContentType {
90    }
91    /**
92     * Content type: unknown.
93     */
94    public static final int CONTENT_TYPE_UNKNOWN = -1;
95
96    /**
97     * Content type: document.
98     * <p>
99     * A print service may use normal paper to print the content instead
100     * of dedicated photo paper. Also it may use a lower quality printing
101     * process as the content is not as sensitive to print quality variation
102     * as a photo is.
103     * </p>
104     */
105    public static final int CONTENT_TYPE_DOCUMENT = 0;
106
107    /**
108     * Content type: photo.
109     * <p>
110     * A print service may use dedicated photo paper to print the content
111     * instead of normal paper. Also it may use a higher quality printing
112     * process as the content is more sensitive to print quality variation
113     * than a document.
114     * </p>
115     */
116    public static final int CONTENT_TYPE_PHOTO = 1;
117
118    private @NonNull String mName;
119    private @IntRange(from = -1) int mPageCount;
120    private int mContentType;
121    private long mDataSize;
122
123    /**
124     * Creates a new instance.
125     */
126    private PrintDocumentInfo() {
127        /* do nothing */
128    }
129
130    /**
131     * Creates a new instance.
132     *
133     * @param prototype from which to clone.
134     */
135    private PrintDocumentInfo(@NonNull PrintDocumentInfo prototype) {
136        mName = prototype.mName;
137        mPageCount = prototype.mPageCount;
138        mContentType = prototype.mContentType;
139        mDataSize = prototype.mDataSize;
140    }
141
142    /**
143     * Creates a new instance.
144     *
145     * @param parcel Data from which to initialize.
146     */
147    private PrintDocumentInfo(Parcel parcel) {
148        mName = Preconditions.checkStringNotEmpty(parcel.readString());
149        mPageCount = parcel.readInt();
150        Preconditions.checkArgument(mPageCount == PAGE_COUNT_UNKNOWN || mPageCount > 0);
151        mContentType = parcel.readInt();
152        mDataSize = Preconditions.checkArgumentNonnegative(parcel.readLong());
153    }
154
155    /**
156     * Gets the document name. This name may be shown to
157     * the user.
158     *
159     * @return The document name.
160     */
161    public @NonNull String getName() {
162        return mName;
163    }
164
165    /**
166     * Gets the total number of pages.
167     *
168     * @return The number of pages.
169     *
170     * @see #PAGE_COUNT_UNKNOWN
171     */
172    public @IntRange(from = -1) int getPageCount() {
173        return mPageCount;
174    }
175
176    /**
177     * Gets the content type.
178     *
179     * @return The content type.
180     *
181     * @see #CONTENT_TYPE_UNKNOWN
182     * @see #CONTENT_TYPE_DOCUMENT
183     * @see #CONTENT_TYPE_PHOTO
184     */
185    public int getContentType() {
186        return mContentType;
187    }
188
189    /**
190     * Gets the document data size in bytes.
191     *
192     * @return The data size.
193     */
194    public @IntRange(from = 0) long getDataSize() {
195        return mDataSize;
196    }
197
198    /**
199     * Sets the document data size in bytes.
200     *
201     * @param dataSize The data size.
202     *
203     * @hide
204     */
205    public void setDataSize(@IntRange(from = 0) long dataSize) {
206        mDataSize = dataSize;
207    }
208
209    @Override
210    public int describeContents() {
211        return 0;
212    }
213
214    @Override
215    public void writeToParcel(Parcel parcel, int flags) {
216        parcel.writeString(mName);
217        parcel.writeInt(mPageCount);
218        parcel.writeInt(mContentType);
219        parcel.writeLong(mDataSize);
220    }
221
222    @Override
223    public int hashCode() {
224        final int prime = 31;
225        int result = 1;
226        result = prime * result + ((mName != null) ? mName.hashCode() : 0);
227        result = prime * result + mContentType;
228        result = prime * result + mPageCount;
229        result = prime * result + (int) mDataSize;
230        result = prime * result + (int) (mDataSize >> 32);
231        return result;
232    }
233
234    @Override
235    public boolean equals(Object obj) {
236        if (this == obj) {
237            return true;
238        }
239        if (obj == null) {
240            return false;
241        }
242        if (getClass() != obj.getClass()) {
243            return false;
244        }
245        PrintDocumentInfo other = (PrintDocumentInfo) obj;
246        if (!TextUtils.equals(mName, other.mName)) {
247            return false;
248        }
249        if (mContentType != other.mContentType) {
250            return false;
251        }
252        if (mPageCount != other.mPageCount) {
253            return false;
254        }
255        if (mDataSize != other.mDataSize) {
256            return false;
257        }
258        return true;
259    }
260
261    @Override
262    public String toString() {
263        StringBuilder builder = new StringBuilder();
264        builder.append("PrintDocumentInfo{");
265        builder.append("name=").append(mName);
266        builder.append(", pageCount=").append(mPageCount);
267        builder.append(", contentType=").append(contentTypeToString(mContentType));
268        builder.append(", dataSize=").append(mDataSize);
269        builder.append("}");
270        return builder.toString();
271    }
272
273    private String contentTypeToString(int contentType) {
274        switch (contentType) {
275            case CONTENT_TYPE_DOCUMENT: {
276                return "CONTENT_TYPE_DOCUMENT";
277            }
278            case CONTENT_TYPE_PHOTO: {
279                return "CONTENT_TYPE_PHOTO";
280            }
281            default: {
282                return "CONTENT_TYPE_UNKNOWN";
283            }
284        }
285    }
286
287    /**
288     * Builder for creating a {@link PrintDocumentInfo}.
289     */
290    public static final class Builder {
291        private final PrintDocumentInfo mPrototype;
292
293        /**
294         * Constructor.
295         *
296         * <p>
297         * The values of the relevant properties are initialized with defaults.
298         * Please refer to the documentation of the individual setters for
299         * information about the default values.
300         * </p>
301         *
302         * @param name The document name which may be shown to the user and
303         * is the file name if the content it describes is saved as a PDF.
304         * Cannot be empty.
305         */
306        public Builder(@NonNull String name) {
307            if (TextUtils.isEmpty(name)) {
308                throw new IllegalArgumentException("name cannot be empty");
309            }
310            mPrototype = new PrintDocumentInfo();
311            mPrototype.mName = name;
312        }
313
314        /**
315         * Sets the total number of pages.
316         * <p>
317         * <strong>Default: </strong> {@link #PAGE_COUNT_UNKNOWN}
318         * </p>
319         *
320         * @param pageCount The number of pages. Must be greater than or equal to zero or
321         *            {@link PrintDocumentInfo#PAGE_COUNT_UNKNOWN}.
322         * @return This builder.
323         */
324        public @NonNull Builder setPageCount(@IntRange(from = -1) int pageCount) {
325            if (pageCount < 0 && pageCount != PAGE_COUNT_UNKNOWN) {
326                throw new IllegalArgumentException("pageCount"
327                        + " must be greater than or equal to zero or"
328                        + " DocumentInfo#PAGE_COUNT_UNKNOWN");
329            }
330            mPrototype.mPageCount = pageCount;
331            return this;
332        }
333
334        /**
335         * Sets the content type.
336         * <p>
337         * <strong>Default: </strong> {@link #CONTENT_TYPE_UNKNOWN}
338         * </p>
339         *
340         * @param type The content type.
341         * @return This builder.
342         * @see #CONTENT_TYPE_UNKNOWN
343         * @see #CONTENT_TYPE_DOCUMENT
344         * @see #CONTENT_TYPE_PHOTO
345         */
346        public @NonNull Builder setContentType(@ContentType int type) {
347            mPrototype.mContentType = type;
348            return this;
349        }
350
351        /**
352         * Creates a new {@link PrintDocumentInfo} instance.
353         *
354         * @return The new instance.
355         */
356        public @NonNull PrintDocumentInfo build() {
357            // Zero pages is the same as unknown as in this case
358            // we will have to ask for all pages and look a the
359            // wiritten PDF file for the page count.
360            if (mPrototype.mPageCount == 0) {
361                mPrototype.mPageCount = PAGE_COUNT_UNKNOWN;
362            }
363            return new PrintDocumentInfo(mPrototype);
364        }
365    }
366
367    public static final Parcelable.Creator<PrintDocumentInfo> CREATOR =
368            new Creator<PrintDocumentInfo>() {
369        @Override
370        public PrintDocumentInfo createFromParcel(Parcel parcel) {
371            return new PrintDocumentInfo(parcel);
372        }
373
374        @Override
375        public PrintDocumentInfo[] newArray(int size) {
376            return new PrintDocumentInfo[size];
377        }
378    };
379}
380