blob: e436bc6ea30f79d8ed9dca9145a0fb907844c0aa [file] [log] [blame]
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001/*
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
Philip P. Moltmannc43639c2015-12-18 13:58:40 -080019import android.annotation.NonNull;
20import android.annotation.Nullable;
Jeff Sharkey98af2e42018-02-16 10:14:57 -070021import android.annotation.RequiresFeature;
Philip P. Moltmann68700332017-06-19 10:55:09 -070022import android.annotation.RequiresPermission;
Philip P. Moltmann7e018952017-04-02 14:34:09 -070023import android.annotation.SystemApi;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060024import android.annotation.SystemService;
Svetoslav Ganov858a1852013-10-17 22:20:40 -070025import android.app.Activity;
26import android.app.Application.ActivityLifecycleCallbacks;
Philip P. Moltmann66c96592016-02-24 11:32:43 -080027import android.content.ComponentName;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070028import android.content.Context;
29import android.content.IntentSender;
30import android.content.IntentSender.SendIntentException;
Jeff Sharkey98af2e42018-02-16 10:14:57 -070031import android.content.pm.PackageManager;
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -080032import android.graphics.drawable.Icon;
Svetoslav62836082013-07-17 14:52:35 -070033import android.os.Bundle;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070034import android.os.CancellationSignal;
35import android.os.Handler;
Svetoslava798c0a2014-05-15 10:47:19 -070036import android.os.ICancellationSignal;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070037import android.os.Looper;
38import android.os.Message;
39import android.os.ParcelFileDescriptor;
40import android.os.RemoteException;
Svetoslav Ganova0027152013-06-25 14:59:53 -070041import android.print.PrintDocumentAdapter.LayoutResultCallback;
42import android.print.PrintDocumentAdapter.WriteResultCallback;
Svetoslav Ganov860f8a62013-09-14 00:59:03 -070043import android.printservice.PrintServiceInfo;
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -070044import android.printservice.recommendation.IRecommendationsChangeListener;
45import android.printservice.recommendation.RecommendationInfo;
Svetoslav Ganov88d19912013-07-22 12:32:03 -070046import android.text.TextUtils;
Svetoslav Ganov704697b2013-09-21 20:30:24 -070047import android.util.ArrayMap;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070048import android.util.Log;
49
50import com.android.internal.os.SomeArgs;
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -070051import com.android.internal.util.Preconditions;
Philip P. Moltmann7e018952017-04-02 14:34:09 -070052
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070053import libcore.io.IoUtils;
54
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070055import java.lang.ref.WeakReference;
56import java.util.ArrayList;
Svetoslava798c0a2014-05-15 10:47:19 -070057import java.util.Arrays;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070058import java.util.Collections;
59import java.util.List;
Svetoslav Ganov704697b2013-09-21 20:30:24 -070060import java.util.Map;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070061
62/**
63 * System level service for accessing the printing capabilities of the platform.
Svetoslava798c0a2014-05-15 10:47:19 -070064 *
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -070065 * <h3>Print mechanics</h3>
66 * <p>
67 * The key idea behind printing on the platform is that the content to be printed
68 * should be laid out for the currently selected print options resulting in an
69 * optimized output and higher user satisfaction. To achieve this goal the platform
70 * declares a contract that the printing application has to follow which is defined
71 * by the {@link PrintDocumentAdapter} class. At a higher level the contract is that
72 * when the user selects some options from the print UI that may affect the way
73 * content is laid out, for example page size, the application receives a callback
74 * allowing it to layout the content to better fit these new constraints. After a
75 * layout pass the system may ask the application to render one or more pages one
76 * or more times. For example, an application may produce a single column list for
77 * smaller page sizes and a multi-column table for larger page sizes.
78 * </p>
79 * <h3>Print jobs</h3>
80 * <p>
81 * Print jobs are started by calling the {@link #print(String, PrintDocumentAdapter,
82 * PrintAttributes)} from an activity which results in bringing up the system print
83 * UI. Once the print UI is up, when the user changes a selected print option that
84 * affects the way content is laid out the system starts to interact with the
85 * application following the mechanics described the section above.
86 * </p>
87 * <p>
88 * Print jobs can be in {@link PrintJobInfo#STATE_CREATED created}, {@link
89 * PrintJobInfo#STATE_QUEUED queued}, {@link PrintJobInfo#STATE_STARTED started},
90 * {@link PrintJobInfo#STATE_BLOCKED blocked}, {@link PrintJobInfo#STATE_COMPLETED
91 * completed}, {@link PrintJobInfo#STATE_FAILED failed}, and {@link
92 * PrintJobInfo#STATE_CANCELED canceled} state. Print jobs are stored in dedicated
93 * system spooler until they are handled which is they are cancelled or completed.
94 * Active print jobs, ones that are not cancelled or completed, are considered failed
95 * if the device reboots as the new boot may be after a very long time. The user may
96 * choose to restart such print jobs. Once a print job is queued all relevant content
97 * is stored in the system spooler and its lifecycle becomes detached from this of
98 * the application that created it.
99 * </p>
100 * <p>
101 * An applications can query the print spooler for current print jobs it created
102 * but not print jobs created by other applications.
103 * </p>
104 *
105 * @see PrintJob
106 * @see PrintJobInfo
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700107 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -0600108@SystemService(Context.PRINT_SERVICE)
Jeff Sharkey98af2e42018-02-16 10:14:57 -0700109@RequiresFeature(PackageManager.FEATURE_PRINTING)
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700110public final class PrintManager {
111
112 private static final String LOG_TAG = "PrintManager";
113
Svetoslav15cbc8a2014-07-11 09:45:07 -0700114 private static final boolean DEBUG = false;
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700115
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700116 private static final int MSG_NOTIFY_PRINT_JOB_STATE_CHANGED = 1;
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800117
118 /**
119 * Package name of print spooler.
120 *
121 * @hide
122 */
123 public static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
124
125 /**
126 * Select enabled services.
127 * </p>
128 * @see #getPrintServices
129 * @hide
130 */
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700131 @SystemApi
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800132 public static final int ENABLED_SERVICES = 1 << 0;
133
134 /**
135 * Select disabled services.
136 * </p>
137 * @see #getPrintServices
138 * @hide
139 */
140 public static final int DISABLED_SERVICES = 1 << 1;
141
142 /**
143 * Select all services.
144 * </p>
145 * @see #getPrintServices
146 * @hide
147 */
148 public static final int ALL_SERVICES = ENABLED_SERVICES | DISABLED_SERVICES;
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700149
150 /**
151 * The action for launching the print dialog activity.
152 *
153 * @hide
154 */
155 public static final String ACTION_PRINT_DIALOG = "android.print.PRINT_DIALOG";
156
157 /**
158 * Extra with the intent for starting the print dialog.
159 * <p>
160 * <strong>Type:</strong> {@link android.content.IntentSender}
161 * </p>
162 *
163 * @hide
164 */
165 public static final String EXTRA_PRINT_DIALOG_INTENT =
166 "android.print.intent.extra.EXTRA_PRINT_DIALOG_INTENT";
167
168 /**
169 * Extra with a print job.
170 * <p>
171 * <strong>Type:</strong> {@link android.print.PrintJobInfo}
172 * </p>
173 *
174 * @hide
175 */
176 public static final String EXTRA_PRINT_JOB =
177 "android.print.intent.extra.EXTRA_PRINT_JOB";
178
179 /**
180 * Extra with the print document adapter to be printed.
181 * <p>
182 * <strong>Type:</strong> {@link android.print.IPrintDocumentAdapter}
183 * </p>
184 *
185 * @hide
186 */
187 public static final String EXTRA_PRINT_DOCUMENT_ADAPTER =
188 "android.print.intent.extra.EXTRA_PRINT_DOCUMENT_ADAPTER";
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700189
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700190 /** @hide */
191 public static final int APP_ID_ANY = -2;
192
193 private final Context mContext;
194
195 private final IPrintManager mService;
196
197 private final int mUserId;
198
199 private final int mAppId;
200
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700201 private final Handler mHandler;
202
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800203 private Map<PrintJobStateChangeListener, PrintJobStateChangeListenerWrapper>
204 mPrintJobStateChangeListeners;
205 private Map<PrintServicesChangeListener, PrintServicesChangeListenerWrapper>
206 mPrintServicesChangeListeners;
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700207 private Map<PrintServiceRecommendationsChangeListener,
208 PrintServiceRecommendationsChangeListenerWrapper>
209 mPrintServiceRecommendationsChangeListeners;
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700210
211 /** @hide */
212 public interface PrintJobStateChangeListener {
213
214 /**
215 * Callback notifying that a print job state changed.
Svetoslav2235a172014-05-08 14:19:01 -0700216 *
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700217 * @param printJobId The print job id.
218 */
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700219 public void onPrintJobStateChanged(PrintJobId printJobId);
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700220 }
221
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700222 /**
223 * Listen for changes to {@link #getPrintServices(int)}.
224 *
225 * @hide
226 */
227 @SystemApi
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800228 public interface PrintServicesChangeListener {
229
230 /**
231 * Callback notifying that the print services changed.
232 */
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700233 void onPrintServicesChanged();
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800234 }
235
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700236 /**
237 * Listen for changes to {@link #getPrintServiceRecommendations()}.
238 *
239 * @hide
240 */
241 @SystemApi
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700242 public interface PrintServiceRecommendationsChangeListener {
243
244 /**
245 * Callback notifying that the print service recommendations changed.
246 */
247 void onPrintServiceRecommendationsChanged();
248 }
249
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700250 /**
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700251 * Creates a new instance.
Svetoslav2235a172014-05-08 14:19:01 -0700252 *
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700253 * @param context The current context in which to operate.
254 * @param service The backing system service.
Philip P. Moltmannc43639c2015-12-18 13:58:40 -0800255 * @param userId The user id in which to operate.
256 * @param appId The application id in which to operate.
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700257 * @hide
258 */
259 public PrintManager(Context context, IPrintManager service, int userId, int appId) {
260 mContext = context;
261 mService = service;
262 mUserId = userId;
263 mAppId = appId;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700264 mHandler = new Handler(context.getMainLooper(), null, false) {
265 @Override
266 public void handleMessage(Message message) {
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700267 switch (message.what) {
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700268 case MSG_NOTIFY_PRINT_JOB_STATE_CHANGED: {
269 SomeArgs args = (SomeArgs) message.obj;
Svetoslav Ganovd91cb3e2013-10-12 15:44:42 -0700270 PrintJobStateChangeListenerWrapper wrapper =
271 (PrintJobStateChangeListenerWrapper) args.arg1;
272 PrintJobStateChangeListener listener = wrapper.getListener();
273 if (listener != null) {
274 PrintJobId printJobId = (PrintJobId) args.arg2;
275 listener.onPrintJobStateChanged(printJobId);
276 }
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700277 args.recycle();
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700278 } break;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700279 }
280 }
281 };
282 }
283
284 /**
285 * Creates an instance that can access all print jobs.
Svetoslav2235a172014-05-08 14:19:01 -0700286 *
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700287 * @param userId The user id for which to get all print jobs.
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700288 * @return An instance if the caller has the permission to access all print
289 * jobs, null otherwise.
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700290 * @hide
291 */
292 public PrintManager getGlobalPrintManagerForUser(int userId) {
Svetoslav2235a172014-05-08 14:19:01 -0700293 if (mService == null) {
294 Log.w(LOG_TAG, "Feature android.software.print not available");
295 return null;
296 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700297 return new PrintManager(mContext, mService, userId, APP_ID_ANY);
298 }
299
Svetoslav2fbd2a72013-09-16 17:53:51 -0700300 PrintJobInfo getPrintJobInfo(PrintJobId printJobId) {
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700301 try {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700302 return mService.getPrintJobInfo(printJobId, mAppId, mUserId);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700303 } catch (RemoteException re) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700304 throw re.rethrowFromSystemServer();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700305 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700306 }
307
308 /**
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700309 * Adds a listener for observing the state of print jobs.
Svetoslav2235a172014-05-08 14:19:01 -0700310 *
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700311 * @param listener The listener to add.
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700312 * @hide
313 */
314 public void addPrintJobStateChangeListener(PrintJobStateChangeListener listener) {
Svetoslav2235a172014-05-08 14:19:01 -0700315 if (mService == null) {
316 Log.w(LOG_TAG, "Feature android.software.print not available");
317 return;
318 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700319 if (mPrintJobStateChangeListeners == null) {
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700320 mPrintJobStateChangeListeners = new ArrayMap<>();
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700321 }
322 PrintJobStateChangeListenerWrapper wrappedListener =
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700323 new PrintJobStateChangeListenerWrapper(listener, mHandler);
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700324 try {
325 mService.addPrintJobStateChangeListener(wrappedListener, mAppId, mUserId);
326 mPrintJobStateChangeListeners.put(listener, wrappedListener);
327 } catch (RemoteException re) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700328 throw re.rethrowFromSystemServer();
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700329 }
330 }
331
332 /**
333 * Removes a listener for observing the state of print jobs.
Svetoslav2235a172014-05-08 14:19:01 -0700334 *
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700335 * @param listener The listener to remove.
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700336 * @hide
337 */
338 public void removePrintJobStateChangeListener(PrintJobStateChangeListener listener) {
Svetoslav2235a172014-05-08 14:19:01 -0700339 if (mService == null) {
340 Log.w(LOG_TAG, "Feature android.software.print not available");
341 return;
342 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700343 if (mPrintJobStateChangeListeners == null) {
344 return;
345 }
346 PrintJobStateChangeListenerWrapper wrappedListener =
347 mPrintJobStateChangeListeners.remove(listener);
348 if (wrappedListener == null) {
349 return;
350 }
351 if (mPrintJobStateChangeListeners.isEmpty()) {
352 mPrintJobStateChangeListeners = null;
353 }
Svetoslav Ganovd91cb3e2013-10-12 15:44:42 -0700354 wrappedListener.destroy();
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700355 try {
356 mService.removePrintJobStateChangeListener(wrappedListener, mUserId);
357 } catch (RemoteException re) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700358 throw re.rethrowFromSystemServer();
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700359 }
360 }
361
362 /**
363 * Gets a print job given its id.
Svetoslav2235a172014-05-08 14:19:01 -0700364 *
Philip P. Moltmannc43639c2015-12-18 13:58:40 -0800365 * @param printJobId The id of the print job.
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700366 * @return The print job list.
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700367 * @see PrintJob
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700368 * @hide
369 */
370 public PrintJob getPrintJob(PrintJobId printJobId) {
Svetoslav2235a172014-05-08 14:19:01 -0700371 if (mService == null) {
372 Log.w(LOG_TAG, "Feature android.software.print not available");
373 return null;
374 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700375 try {
376 PrintJobInfo printJob = mService.getPrintJobInfo(printJobId, mAppId, mUserId);
377 if (printJob != null) {
378 return new PrintJob(printJob, this);
379 }
380 } catch (RemoteException re) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700381 throw re.rethrowFromSystemServer();
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700382 }
383 return null;
384 }
385
386 /**
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800387 * Get the custom icon for a printer. If the icon is not cached, the icon is
388 * requested asynchronously. Once it is available the printer is updated.
389 *
390 * @param printerId the id of the printer the icon should be loaded for
391 * @return the custom icon to be used for the printer or null if the icon is
392 * not yet available
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700393 * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon(boolean)
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800394 * @hide
395 */
396 public Icon getCustomPrinterIcon(PrinterId printerId) {
397 if (mService == null) {
398 Log.w(LOG_TAG, "Feature android.software.print not available");
399 return null;
400 }
401 try {
402 return mService.getCustomPrinterIcon(printerId, mUserId);
403 } catch (RemoteException re) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700404 throw re.rethrowFromSystemServer();
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800405 }
Philip P. Moltmannbb9f6862015-12-01 14:44:24 -0800406 }
407
408 /**
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700409 * Gets the print jobs for this application.
Svetoslav2235a172014-05-08 14:19:01 -0700410 *
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700411 * @return The print job list.
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700412 * @see PrintJob
413 */
Philip P. Moltmannc43639c2015-12-18 13:58:40 -0800414 public @NonNull List<PrintJob> getPrintJobs() {
Svetoslav2235a172014-05-08 14:19:01 -0700415 if (mService == null) {
416 Log.w(LOG_TAG, "Feature android.software.print not available");
417 return Collections.emptyList();
418 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700419 try {
Svetoslav Ganova0027152013-06-25 14:59:53 -0700420 List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700421 if (printJobInfos == null) {
422 return Collections.emptyList();
423 }
424 final int printJobCount = printJobInfos.size();
425 List<PrintJob> printJobs = new ArrayList<PrintJob>(printJobCount);
426 for (int i = 0; i < printJobCount; i++) {
427 printJobs.add(new PrintJob(printJobInfos.get(i), this));
428 }
429 return printJobs;
430 } catch (RemoteException re) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700431 throw re.rethrowFromSystemServer();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700432 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700433 }
434
Svetoslav2fbd2a72013-09-16 17:53:51 -0700435 void cancelPrintJob(PrintJobId printJobId) {
Svetoslav2235a172014-05-08 14:19:01 -0700436 if (mService == null) {
437 Log.w(LOG_TAG, "Feature android.software.print not available");
438 return;
439 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700440 try {
441 mService.cancelPrintJob(printJobId, mAppId, mUserId);
442 } catch (RemoteException re) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700443 throw re.rethrowFromSystemServer();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700444 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700445 }
446
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700447 void restartPrintJob(PrintJobId printJobId) {
Svetoslav2235a172014-05-08 14:19:01 -0700448 if (mService == null) {
449 Log.w(LOG_TAG, "Feature android.software.print not available");
450 return;
451 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700452 try {
453 mService.restartPrintJob(printJobId, mAppId, mUserId);
454 } catch (RemoteException re) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700455 throw re.rethrowFromSystemServer();
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700456 }
457 }
458
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700459 /**
Svetoslav Ganova18661d2013-10-09 22:55:49 -0700460 * Creates a print job for printing a {@link PrintDocumentAdapter} with
461 * default print attributes.
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700462 * <p>
463 * Calling this method brings the print UI allowing the user to customize
464 * the print job and returns a {@link PrintJob} object without waiting for the
465 * user to customize or confirm the print job. The returned print job instance
466 * is in a {@link PrintJobInfo#STATE_CREATED created} state.
467 * <p>
468 * This method can be called only from an {@link Activity}. The rationale is that
469 * printing from a service will create an inconsistent user experience as the print
470 * UI would appear without any context.
471 * </p>
472 * <p>
473 * Also the passed in {@link PrintDocumentAdapter} will be considered invalid if
474 * your activity is finished. The rationale is that once the activity that
475 * initiated printing is finished, the provided adapter may be in an inconsistent
476 * state as it may depend on the UI presented by the activity.
477 * </p>
478 * <p>
479 * The default print attributes are a hint to the system how the data is to
480 * be printed. For example, a photo editor may look at the photo aspect ratio
481 * to determine the default orientation and provide a hint whether the printing
482 * should be in portrait or landscape. The system will do a best effort to
483 * selected the hinted options in the print dialog, given the current printer
484 * supports them.
485 * </p>
Svetoslav81f14b92014-03-18 10:37:20 -0700486 * <p>
487 * <strong>Note:</strong> Calling this method will bring the print dialog and
488 * the system will connect to the provided {@link PrintDocumentAdapter}. If a
Svetoslav85a85a02014-03-18 10:48:51 -0700489 * configuration change occurs that you application does not handle, for example
490 * a rotation change, the system will drop the connection to the adapter as the
491 * activity has to be recreated and the old adapter may be invalid in this context,
492 * hence a new adapter instance is required. As a consequence, if your activity
493 * does not handle configuration changes (default behavior), you have to save the
494 * state that you were printing and call this method again when your activity
495 * is recreated.
Svetoslav81f14b92014-03-18 10:37:20 -0700496 * </p>
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700497 *
498 * @param printJobName A name for the new print job which is shown to the user.
Svetoslav Ganova0027152013-06-25 14:59:53 -0700499 * @param documentAdapter An adapter that emits the document to print.
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700500 * @param attributes The default print job attributes or <code>null</code>.
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700501 * @return The created print job on success or null on failure.
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700502 * @throws IllegalStateException If not called from an {@link Activity}.
503 * @throws IllegalArgumentException If the print job name is empty or the
504 * document adapter is null.
505 *
Svetoslavfd906512013-06-24 09:04:48 -0700506 * @see PrintJob
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700507 */
Philip P. Moltmannc43639c2015-12-18 13:58:40 -0800508 public @NonNull PrintJob print(@NonNull String printJobName,
509 @NonNull PrintDocumentAdapter documentAdapter,
510 @Nullable PrintAttributes attributes) {
Svetoslav2235a172014-05-08 14:19:01 -0700511 if (mService == null) {
512 Log.w(LOG_TAG, "Feature android.software.print not available");
513 return null;
514 }
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700515 if (!(mContext instanceof Activity)) {
516 throw new IllegalStateException("Can print only from an activity");
517 }
Svetoslav Ganov88d19912013-07-22 12:32:03 -0700518 if (TextUtils.isEmpty(printJobName)) {
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700519 throw new IllegalArgumentException("printJobName cannot be empty");
520 }
521 if (documentAdapter == null) {
522 throw new IllegalArgumentException("documentAdapter cannot be null");
Svetoslav Ganov88d19912013-07-22 12:32:03 -0700523 }
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700524 PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700525 (Activity) mContext, documentAdapter);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700526 try {
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700527 Bundle result = mService.print(printJobName, delegate,
528 attributes, mContext.getPackageName(), mAppId, mUserId);
529 if (result != null) {
530 PrintJobInfo printJob = result.getParcelable(EXTRA_PRINT_JOB);
531 IntentSender intent = result.getParcelable(EXTRA_PRINT_DIALOG_INTENT);
532 if (printJob == null || intent == null) {
533 return null;
534 }
535 try {
536 mContext.startIntentSender(intent, null, 0, 0, 0);
537 return new PrintJob(printJob, this);
538 } catch (SendIntentException sie) {
539 Log.e(LOG_TAG, "Couldn't start print job config activity.", sie);
540 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700541 }
542 } catch (RemoteException re) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700543 throw re.rethrowFromSystemServer();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700544 }
545 return null;
546 }
547
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700548 /**
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800549 * Listen for changes to the installed and enabled print services.
Svetoslav2235a172014-05-08 14:19:01 -0700550 *
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800551 * @param listener the listener to add
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700552 * @param handler the handler the listener is called back on
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800553 *
554 * @see android.print.PrintManager#getPrintServices
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700555 *
556 * @hide
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700557 */
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700558 @SystemApi
Philip P. Moltmann68700332017-06-19 10:55:09 -0700559 @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICES)
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700560 public void addPrintServicesChangeListener(@NonNull PrintServicesChangeListener listener,
561 @Nullable Handler handler) {
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700562 Preconditions.checkNotNull(listener);
563
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700564 if (handler == null) {
565 handler = mHandler;
566 }
567
Svetoslav2235a172014-05-08 14:19:01 -0700568 if (mService == null) {
569 Log.w(LOG_TAG, "Feature android.software.print not available");
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800570 return;
Svetoslav2235a172014-05-08 14:19:01 -0700571 }
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800572 if (mPrintServicesChangeListeners == null) {
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700573 mPrintServicesChangeListeners = new ArrayMap<>();
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800574 }
575 PrintServicesChangeListenerWrapper wrappedListener =
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700576 new PrintServicesChangeListenerWrapper(listener, handler);
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700577 try {
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800578 mService.addPrintServicesChangeListener(wrappedListener, mUserId);
579 mPrintServicesChangeListeners.put(listener, wrappedListener);
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700580 } catch (RemoteException re) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700581 throw re.rethrowFromSystemServer();
Svetoslavd8dbc132013-09-27 18:29:53 -0700582 }
Svetoslavd8dbc132013-09-27 18:29:53 -0700583 }
584
585 /**
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800586 * Stop listening for changes to the installed and enabled print services.
Svetoslav2235a172014-05-08 14:19:01 -0700587 *
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800588 * @param listener the listener to remove
589 *
590 * @see android.print.PrintManager#getPrintServices
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700591 *
592 * @hide
Svetoslavd8dbc132013-09-27 18:29:53 -0700593 */
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700594 @SystemApi
Philip P. Moltmann68700332017-06-19 10:55:09 -0700595 @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICES)
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700596 public void removePrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) {
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700597 Preconditions.checkNotNull(listener);
598
Svetoslav2235a172014-05-08 14:19:01 -0700599 if (mService == null) {
600 Log.w(LOG_TAG, "Feature android.software.print not available");
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800601 return;
Svetoslav2235a172014-05-08 14:19:01 -0700602 }
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800603 if (mPrintServicesChangeListeners == null) {
604 return;
605 }
606 PrintServicesChangeListenerWrapper wrappedListener =
607 mPrintServicesChangeListeners.remove(listener);
608 if (wrappedListener == null) {
609 return;
610 }
611 if (mPrintServicesChangeListeners.isEmpty()) {
612 mPrintServicesChangeListeners = null;
613 }
614 wrappedListener.destroy();
Svetoslavd8dbc132013-09-27 18:29:53 -0700615 try {
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800616 mService.removePrintServicesChangeListener(wrappedListener, mUserId);
617 } catch (RemoteException re) {
618 Log.e(LOG_TAG, "Error removing print services change listener", re);
619 }
620 }
621
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800622 /**
623 * Gets the list of print services, but does not register for updates. The user has to register
624 * for updates by itself, or use {@link PrintServicesLoader}.
625 *
626 * @param selectionFlags flags selecting which services to get. Either
627 * {@link #ENABLED_SERVICES},{@link #DISABLED_SERVICES}, or both.
628 *
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700629 * @return The print service list or an empty list.
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800630 *
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700631 * @see #addPrintServicesChangeListener(PrintServicesChangeListener, Handler)
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800632 * @see #removePrintServicesChangeListener(PrintServicesChangeListener)
633 *
634 * @hide
635 */
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700636 @SystemApi
Philip P. Moltmann68700332017-06-19 10:55:09 -0700637 @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICES)
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800638 public @NonNull List<PrintServiceInfo> getPrintServices(int selectionFlags) {
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700639 Preconditions.checkFlagsArgument(selectionFlags, ALL_SERVICES);
640
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800641 try {
642 List<PrintServiceInfo> services = mService.getPrintServices(selectionFlags, mUserId);
643 if (services != null) {
644 return services;
Svetoslavd8dbc132013-09-27 18:29:53 -0700645 }
646 } catch (RemoteException re) {
Jeff Sharkeyf8880562016-02-26 13:03:01 -0700647 throw re.rethrowFromSystemServer();
Svetoslav Ganov860f8a62013-09-14 00:59:03 -0700648 }
649 return Collections.emptyList();
650 }
651
652 /**
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700653 * Listen for changes to the print service recommendations.
654 *
655 * @param listener the listener to add
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700656 * @param handler the handler the listener is called back on
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700657 *
658 * @see android.print.PrintManager#getPrintServiceRecommendations
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700659 *
660 * @hide
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700661 */
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700662 @SystemApi
Philip P. Moltmann68700332017-06-19 10:55:09 -0700663 @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS)
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700664 public void addPrintServiceRecommendationsChangeListener(
665 @NonNull PrintServiceRecommendationsChangeListener listener,
666 @Nullable Handler handler) {
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700667 Preconditions.checkNotNull(listener);
668
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700669 if (handler == null) {
670 handler = mHandler;
671 }
672
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700673 if (mService == null) {
674 Log.w(LOG_TAG, "Feature android.software.print not available");
675 return;
676 }
677 if (mPrintServiceRecommendationsChangeListeners == null) {
678 mPrintServiceRecommendationsChangeListeners = new ArrayMap<>();
679 }
680 PrintServiceRecommendationsChangeListenerWrapper wrappedListener =
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700681 new PrintServiceRecommendationsChangeListenerWrapper(listener, handler);
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700682 try {
683 mService.addPrintServiceRecommendationsChangeListener(wrappedListener, mUserId);
684 mPrintServiceRecommendationsChangeListeners.put(listener, wrappedListener);
685 } catch (RemoteException re) {
686 throw re.rethrowFromSystemServer();
687 }
688 }
689
690 /**
691 * Stop listening for changes to the print service recommendations.
692 *
693 * @param listener the listener to remove
694 *
695 * @see android.print.PrintManager#getPrintServiceRecommendations
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700696 *
697 * @hide
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700698 */
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700699 @SystemApi
Philip P. Moltmann68700332017-06-19 10:55:09 -0700700 @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS)
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700701 public void removePrintServiceRecommendationsChangeListener(
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700702 @NonNull PrintServiceRecommendationsChangeListener listener) {
703 Preconditions.checkNotNull(listener);
704
705 if (mService == null) {
706 Log.w(LOG_TAG, "Feature android.software.print not available");
707 return;
708 }
709 if (mPrintServiceRecommendationsChangeListeners == null) {
710 return;
711 }
712 PrintServiceRecommendationsChangeListenerWrapper wrappedListener =
713 mPrintServiceRecommendationsChangeListeners.remove(listener);
714 if (wrappedListener == null) {
715 return;
716 }
717 if (mPrintServiceRecommendationsChangeListeners.isEmpty()) {
718 mPrintServiceRecommendationsChangeListeners = null;
719 }
720 wrappedListener.destroy();
721 try {
722 mService.removePrintServiceRecommendationsChangeListener(wrappedListener, mUserId);
723 } catch (RemoteException re) {
724 throw re.rethrowFromSystemServer();
725 }
726 }
727
728 /**
729 * Gets the list of print service recommendations, but does not register for updates. The user
730 * has to register for updates by itself, or use {@link PrintServiceRecommendationsLoader}.
731 *
732 * @return The print service recommendations list or an empty list.
733 *
734 * @see #addPrintServiceRecommendationsChangeListener
735 * @see #removePrintServiceRecommendationsChangeListener
736 *
737 * @hide
738 */
Philip P. Moltmann7e018952017-04-02 14:34:09 -0700739 @SystemApi
Philip P. Moltmann68700332017-06-19 10:55:09 -0700740 @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS)
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -0700741 public @NonNull List<RecommendationInfo> getPrintServiceRecommendations() {
742 try {
743 List<RecommendationInfo> recommendations =
744 mService.getPrintServiceRecommendations(mUserId);
745 if (recommendations != null) {
746 return recommendations;
747 }
748 } catch (RemoteException re) {
749 throw re.rethrowFromSystemServer();
750 }
751 return Collections.emptyList();
752 }
753
754 /**
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700755 * @hide
756 */
757 public PrinterDiscoverySession createPrinterDiscoverySession() {
Svetoslav2235a172014-05-08 14:19:01 -0700758 if (mService == null) {
759 Log.w(LOG_TAG, "Feature android.software.print not available");
760 return null;
761 }
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700762 return new PrinterDiscoverySession(mService, mContext, mUserId);
763 }
764
Philip P. Moltmann76d7e3e2016-01-15 13:22:13 -0800765 /**
Philip P. Moltmann66c96592016-02-24 11:32:43 -0800766 * Enable or disable a print service.
767 *
768 * @param service The service to enabled or disable
769 * @param isEnabled whether the service should be enabled or disabled
770 *
771 * @hide
772 */
773 public void setPrintServiceEnabled(@NonNull ComponentName service, boolean isEnabled) {
774 if (mService == null) {
775 Log.w(LOG_TAG, "Feature android.software.print not available");
776 return;
777 }
778 try {
779 mService.setPrintServiceEnabled(service, isEnabled, mUserId);
780 } catch (RemoteException re) {
781 Log.e(LOG_TAG, "Error enabling or disabling " + service, re);
782 }
783 }
784
785 /**
Philip P. Moltmann76d7e3e2016-01-15 13:22:13 -0800786 * @hide
787 */
788 public static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700789 implements ActivityLifecycleCallbacks {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700790 private final Object mLock = new Object();
791
Svetoslava798c0a2014-05-15 10:47:19 -0700792 private Activity mActivity; // Strong reference OK - cleared in destroy
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700793
Svetoslava798c0a2014-05-15 10:47:19 -0700794 private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in destroy
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700795
Svetoslava798c0a2014-05-15 10:47:19 -0700796 private Handler mHandler; // Strong reference OK - cleared in destroy
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700797
Svetoslava798c0a2014-05-15 10:47:19 -0700798 private IPrintDocumentAdapterObserver mObserver; // Strong reference OK - cleared in destroy
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700799
Svetoslava798c0a2014-05-15 10:47:19 -0700800 private DestroyableCallback mPendingCallback;
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700801
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700802 public PrintDocumentAdapterDelegate(Activity activity,
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700803 PrintDocumentAdapter documentAdapter) {
Philip P. Moltmannc8634892016-04-20 11:53:57 -0700804 if (activity.isFinishing()) {
805 // The activity is already dead hence the onActivityDestroyed callback won't be
806 // triggered. Hence it is not save to print in this situation.
807 throw new IllegalStateException("Cannot start printing for finishing activity");
808 }
809
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700810 mActivity = activity;
Svetoslav Ganova0027152013-06-25 14:59:53 -0700811 mDocumentAdapter = documentAdapter;
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700812 mHandler = new MyHandler(mActivity.getMainLooper());
813 mActivity.getApplication().registerActivityLifecycleCallbacks(this);
814 }
815
816 @Override
817 public void setObserver(IPrintDocumentAdapterObserver observer) {
818 final boolean destroyed;
819 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -0700820 mObserver = observer;
821 destroyed = isDestroyedLocked();
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700822 }
Svetoslava798c0a2014-05-15 10:47:19 -0700823
Svetoslav6552bf32014-09-03 21:15:55 -0700824 if (destroyed && observer != null) {
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700825 try {
826 observer.onDestroy();
827 } catch (RemoteException re) {
828 Log.e(LOG_TAG, "Error announcing destroyed state", re);
829 }
830 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700831 }
832
833 @Override
834 public void start() {
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700835 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -0700836 // If destroyed the handler is null.
837 if (!isDestroyedLocked()) {
838 mHandler.obtainMessage(MyHandler.MSG_ON_START,
839 mDocumentAdapter).sendToTarget();
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700840 }
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700841 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700842 }
843
844 @Override
Svetoslav62836082013-07-17 14:52:35 -0700845 public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700846 ILayoutResultCallback callback, Bundle metadata, int sequence) {
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700847
Svetoslava798c0a2014-05-15 10:47:19 -0700848 ICancellationSignal cancellationTransport = CancellationSignal.createTransport();
849 try {
850 callback.onLayoutStarted(cancellationTransport, sequence);
851 } catch (RemoteException re) {
852 // The spooler is dead - can't recover.
853 Log.e(LOG_TAG, "Error notifying for layout start", re);
854 return;
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700855 }
Svetoslava798c0a2014-05-15 10:47:19 -0700856
857 synchronized (mLock) {
858 // If destroyed the handler is null.
859 if (isDestroyedLocked()) {
860 return;
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700861 }
Svetoslava798c0a2014-05-15 10:47:19 -0700862
863 CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
864 cancellationTransport);
865
866 SomeArgs args = SomeArgs.obtain();
867 args.arg1 = mDocumentAdapter;
868 args.arg2 = oldAttributes;
869 args.arg3 = newAttributes;
870 args.arg4 = cancellationSignal;
871 args.arg5 = new MyLayoutResultCallback(callback, sequence);
872 args.arg6 = metadata;
873
874 mHandler.obtainMessage(MyHandler.MSG_ON_LAYOUT, args).sendToTarget();
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700875 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700876 }
877
878 @Override
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700879 public void write(PageRange[] pages, ParcelFileDescriptor fd,
Svetoslav Ganov704697b2013-09-21 20:30:24 -0700880 IWriteResultCallback callback, int sequence) {
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700881
Svetoslava798c0a2014-05-15 10:47:19 -0700882 ICancellationSignal cancellationTransport = CancellationSignal.createTransport();
883 try {
884 callback.onWriteStarted(cancellationTransport, sequence);
885 } catch (RemoteException re) {
886 // The spooler is dead - can't recover.
887 Log.e(LOG_TAG, "Error notifying for write start", re);
888 return;
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700889 }
Svetoslava798c0a2014-05-15 10:47:19 -0700890
891 synchronized (mLock) {
892 // If destroyed the handler is null.
893 if (isDestroyedLocked()) {
894 return;
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700895 }
Svetoslava798c0a2014-05-15 10:47:19 -0700896
897 CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
898 cancellationTransport);
899
900 SomeArgs args = SomeArgs.obtain();
901 args.arg1 = mDocumentAdapter;
902 args.arg2 = pages;
903 args.arg3 = fd;
904 args.arg4 = cancellationSignal;
905 args.arg5 = new MyWriteResultCallback(callback, fd, sequence);
906
907 mHandler.obtainMessage(MyHandler.MSG_ON_WRITE, args).sendToTarget();
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700908 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700909 }
910
911 @Override
912 public void finish() {
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700913 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -0700914 // If destroyed the handler is null.
915 if (!isDestroyedLocked()) {
916 mHandler.obtainMessage(MyHandler.MSG_ON_FINISH,
917 mDocumentAdapter).sendToTarget();
Svetoslav Ganovdb636772013-10-07 14:31:18 -0700918 }
Svetoslavd270cb92013-10-31 14:27:16 -0700919 }
920 }
921
922 @Override
Svet Ganovfce84f02014-10-31 16:56:52 -0700923 public void kill(String reason) {
924 synchronized (mLock) {
925 // If destroyed the handler is null.
926 if (!isDestroyedLocked()) {
927 mHandler.obtainMessage(MyHandler.MSG_ON_KILL,
928 reason).sendToTarget();
929 }
930 }
931 }
932
933 @Override
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700934 public void onActivityPaused(Activity activity) {
935 /* do nothing */
936 }
937
938 @Override
939 public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
940 /* do nothing */
941 }
942
943 @Override
944 public void onActivityStarted(Activity activity) {
945 /* do nothing */
946 }
947
948 @Override
949 public void onActivityResumed(Activity activity) {
950 /* do nothing */
951 }
952
953 @Override
954 public void onActivityStopped(Activity activity) {
955 /* do nothing */
956 }
957
958 @Override
959 public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
960 /* do nothing */
961 }
962
963 @Override
964 public void onActivityDestroyed(Activity activity) {
965 // We really care only if the activity is being destroyed to
966 // notify the the print spooler so it can close the print dialog.
967 // Note the the spooler has a death recipient that observes if
968 // this process gets killed so we cover the case of onDestroy not
969 // being called due to this process being killed to reclaim memory.
Svetoslava798c0a2014-05-15 10:47:19 -0700970 IPrintDocumentAdapterObserver observer = null;
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700971 synchronized (mLock) {
972 if (activity == mActivity) {
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700973 observer = mObserver;
Svetoslava798c0a2014-05-15 10:47:19 -0700974 destroyLocked();
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700975 }
976 }
977 if (observer != null) {
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700978 try {
979 observer.onDestroy();
980 } catch (RemoteException re) {
981 Log.e(LOG_TAG, "Error announcing destroyed state", re);
982 }
983 }
984 }
985
Svetoslava798c0a2014-05-15 10:47:19 -0700986 private boolean isDestroyedLocked() {
987 return (mActivity == null);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700988 }
989
Svetoslava798c0a2014-05-15 10:47:19 -0700990 private void destroyLocked() {
991 mActivity.getApplication().unregisterActivityLifecycleCallbacks(
992 PrintDocumentAdapterDelegate.this);
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700993 mActivity = null;
Svetoslava798c0a2014-05-15 10:47:19 -0700994
Svetoslav Ganova0027152013-06-25 14:59:53 -0700995 mDocumentAdapter = null;
Svetoslava798c0a2014-05-15 10:47:19 -0700996
997 // This method is only called from the main thread, so
998 // clearing the messages guarantees that any time a
999 // message is handled we are not in a destroyed state.
1000 mHandler.removeMessages(MyHandler.MSG_ON_START);
1001 mHandler.removeMessages(MyHandler.MSG_ON_LAYOUT);
1002 mHandler.removeMessages(MyHandler.MSG_ON_WRITE);
1003 mHandler.removeMessages(MyHandler.MSG_ON_FINISH);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001004 mHandler = null;
Svetoslava798c0a2014-05-15 10:47:19 -07001005
1006 mObserver = null;
1007
1008 if (mPendingCallback != null) {
1009 mPendingCallback.destroy();
1010 mPendingCallback = null;
Svetoslav Ganovdb636772013-10-07 14:31:18 -07001011 }
1012 }
1013
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001014 private final class MyHandler extends Handler {
Svetoslava798c0a2014-05-15 10:47:19 -07001015 public static final int MSG_ON_START = 1;
1016 public static final int MSG_ON_LAYOUT = 2;
1017 public static final int MSG_ON_WRITE = 3;
1018 public static final int MSG_ON_FINISH = 4;
Svet Ganovfce84f02014-10-31 16:56:52 -07001019 public static final int MSG_ON_KILL = 5;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001020
1021 public MyHandler(Looper looper) {
1022 super(looper, null, true);
1023 }
1024
1025 @Override
1026 public void handleMessage(Message message) {
1027 switch (message.what) {
Svetoslava798c0a2014-05-15 10:47:19 -07001028 case MSG_ON_START: {
Svetoslav Ganovdb636772013-10-07 14:31:18 -07001029 if (DEBUG) {
Svetoslava798c0a2014-05-15 10:47:19 -07001030 Log.i(LOG_TAG, "onStart()");
Svetoslav Ganovdb636772013-10-07 14:31:18 -07001031 }
Svetoslava798c0a2014-05-15 10:47:19 -07001032
1033 ((PrintDocumentAdapter) message.obj).onStart();
1034 } break;
1035
1036 case MSG_ON_LAYOUT: {
1037 SomeArgs args = (SomeArgs) message.obj;
1038 PrintDocumentAdapter adapter = (PrintDocumentAdapter) args.arg1;
1039 PrintAttributes oldAttributes = (PrintAttributes) args.arg2;
1040 PrintAttributes newAttributes = (PrintAttributes) args.arg3;
1041 CancellationSignal cancellation = (CancellationSignal) args.arg4;
1042 LayoutResultCallback callback = (LayoutResultCallback) args.arg5;
1043 Bundle metadata = (Bundle) args.arg6;
1044 args.recycle();
1045
1046 if (DEBUG) {
1047 StringBuilder builder = new StringBuilder();
1048 builder.append("PrintDocumentAdapter#onLayout() {\n");
1049 builder.append("\n oldAttributes:").append(oldAttributes);
1050 builder.append("\n newAttributes:").append(newAttributes);
1051 builder.append("\n preview:").append(metadata.getBoolean(
1052 PrintDocumentAdapter.EXTRA_PRINT_PREVIEW));
1053 builder.append("\n}");
1054 Log.i(LOG_TAG, builder.toString());
1055 }
1056
1057 adapter.onLayout(oldAttributes, newAttributes, cancellation,
1058 callback, metadata);
1059 } break;
1060
1061 case MSG_ON_WRITE: {
1062 SomeArgs args = (SomeArgs) message.obj;
1063 PrintDocumentAdapter adapter = (PrintDocumentAdapter) args.arg1;
1064 PageRange[] pages = (PageRange[]) args.arg2;
1065 ParcelFileDescriptor fd = (ParcelFileDescriptor) args.arg3;
1066 CancellationSignal cancellation = (CancellationSignal) args.arg4;
1067 WriteResultCallback callback = (WriteResultCallback) args.arg5;
1068 args.recycle();
1069
1070 if (DEBUG) {
1071 StringBuilder builder = new StringBuilder();
1072 builder.append("PrintDocumentAdapter#onWrite() {\n");
1073 builder.append("\n pages:").append(Arrays.toString(pages));
1074 builder.append("\n}");
1075 Log.i(LOG_TAG, builder.toString());
1076 }
1077
1078 adapter.onWrite(pages, fd, cancellation, callback);
1079 } break;
1080
1081 case MSG_ON_FINISH: {
1082 if (DEBUG) {
1083 Log.i(LOG_TAG, "onFinish()");
1084 }
1085
1086 ((PrintDocumentAdapter) message.obj).onFinish();
1087
1088 // Done printing, so destroy this instance as it
1089 // should not be used anymore.
Svetoslav Ganov858a1852013-10-17 22:20:40 -07001090 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -07001091 destroyLocked();
Svetoslav Ganov858a1852013-10-17 22:20:40 -07001092 }
1093 } break;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001094
Svet Ganovfce84f02014-10-31 16:56:52 -07001095 case MSG_ON_KILL: {
1096 if (DEBUG) {
1097 Log.i(LOG_TAG, "onKill()");
1098 }
1099
1100 String reason = (String) message.obj;
1101 throw new RuntimeException(reason);
1102 }
1103
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001104 default: {
1105 throw new IllegalArgumentException("Unknown message: "
1106 + message.what);
1107 }
1108 }
1109 }
1110 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001111
Svetoslava798c0a2014-05-15 10:47:19 -07001112 private interface DestroyableCallback {
1113 public void destroy();
1114 }
1115
1116 private final class MyLayoutResultCallback extends LayoutResultCallback
1117 implements DestroyableCallback {
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001118 private ILayoutResultCallback mCallback;
1119 private final int mSequence;
1120
1121 public MyLayoutResultCallback(ILayoutResultCallback callback,
1122 int sequence) {
1123 mCallback = callback;
1124 mSequence = sequence;
1125 }
1126
1127 @Override
1128 public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
1129 final ILayoutResultCallback callback;
1130 synchronized (mLock) {
1131 callback = mCallback;
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001132 }
Svetoslava798c0a2014-05-15 10:47:19 -07001133
1134 // If the callback is null we are destroyed.
1135 if (callback == null) {
1136 Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
1137 + "finish the printing activity before print completion "
1138 + "or did you invoke a callback after finish?");
1139 return;
1140 }
1141
1142 try {
1143 if (info == null) {
1144 throw new NullPointerException("document info cannot be null");
1145 }
1146
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001147 try {
1148 callback.onLayoutFinished(info, changed, mSequence);
1149 } catch (RemoteException re) {
1150 Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
1151 }
Svetoslava798c0a2014-05-15 10:47:19 -07001152 } finally {
1153 destroy();
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001154 }
1155 }
1156
1157 @Override
1158 public void onLayoutFailed(CharSequence error) {
1159 final ILayoutResultCallback callback;
1160 synchronized (mLock) {
1161 callback = mCallback;
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001162 }
Svetoslava798c0a2014-05-15 10:47:19 -07001163
1164 // If the callback is null we are destroyed.
1165 if (callback == null) {
1166 Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
1167 + "finish the printing activity before print completion "
1168 + "or did you invoke a callback after finish?");
1169 return;
1170 }
1171
1172 try {
1173 callback.onLayoutFailed(error, mSequence);
1174 } catch (RemoteException re) {
1175 Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
1176 } finally {
1177 destroy();
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001178 }
1179 }
1180
1181 @Override
1182 public void onLayoutCancelled() {
Svetoslava798c0a2014-05-15 10:47:19 -07001183 final ILayoutResultCallback callback;
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001184 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -07001185 callback = mCallback;
1186 }
1187
1188 // If the callback is null we are destroyed.
1189 if (callback == null) {
1190 Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
1191 + "finish the printing activity before print completion "
1192 + "or did you invoke a callback after finish?");
1193 return;
1194 }
1195
1196 try {
1197 callback.onLayoutCanceled(mSequence);
1198 } catch (RemoteException re) {
1199 Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
1200 } finally {
1201 destroy();
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001202 }
1203 }
1204
Svetoslava798c0a2014-05-15 10:47:19 -07001205 @Override
1206 public void destroy() {
1207 synchronized (mLock) {
1208 mCallback = null;
1209 mPendingCallback = null;
1210 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001211 }
1212 }
1213
Svetoslava798c0a2014-05-15 10:47:19 -07001214 private final class MyWriteResultCallback extends WriteResultCallback
1215 implements DestroyableCallback {
Svetoslav Ganovd26d4892013-08-28 14:37:54 -07001216 private ParcelFileDescriptor mFd;
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001217 private IWriteResultCallback mCallback;
Svetoslava798c0a2014-05-15 10:47:19 -07001218 private final int mSequence;
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001219
1220 public MyWriteResultCallback(IWriteResultCallback callback,
Svetoslav Ganovd26d4892013-08-28 14:37:54 -07001221 ParcelFileDescriptor fd, int sequence) {
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001222 mFd = fd;
1223 mSequence = sequence;
1224 mCallback = callback;
1225 }
1226
1227 @Override
1228 public void onWriteFinished(PageRange[] pages) {
1229 final IWriteResultCallback callback;
1230 synchronized (mLock) {
1231 callback = mCallback;
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001232 }
Svetoslava798c0a2014-05-15 10:47:19 -07001233
1234 // If the callback is null we are destroyed.
1235 if (callback == null) {
1236 Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
1237 + "finish the printing activity before print completion "
1238 + "or did you invoke a callback after finish?");
1239 return;
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001240 }
Svetoslava798c0a2014-05-15 10:47:19 -07001241
1242 try {
1243 if (pages == null) {
1244 throw new IllegalArgumentException("pages cannot be null");
1245 }
1246 if (pages.length == 0) {
1247 throw new IllegalArgumentException("pages cannot be empty");
1248 }
1249
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001250 try {
1251 callback.onWriteFinished(pages, mSequence);
1252 } catch (RemoteException re) {
1253 Log.e(LOG_TAG, "Error calling onWriteFinished", re);
1254 }
Svetoslava798c0a2014-05-15 10:47:19 -07001255 } finally {
1256 destroy();
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001257 }
1258 }
1259
1260 @Override
1261 public void onWriteFailed(CharSequence error) {
1262 final IWriteResultCallback callback;
1263 synchronized (mLock) {
1264 callback = mCallback;
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001265 }
Svetoslava798c0a2014-05-15 10:47:19 -07001266
1267 // If the callback is null we are destroyed.
1268 if (callback == null) {
1269 Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
1270 + "finish the printing activity before print completion "
1271 + "or did you invoke a callback after finish?");
1272 return;
1273 }
1274
1275 try {
1276 callback.onWriteFailed(error, mSequence);
1277 } catch (RemoteException re) {
1278 Log.e(LOG_TAG, "Error calling onWriteFailed", re);
1279 } finally {
1280 destroy();
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001281 }
1282 }
1283
1284 @Override
1285 public void onWriteCancelled() {
Svetoslava798c0a2014-05-15 10:47:19 -07001286 final IWriteResultCallback callback;
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001287 synchronized (mLock) {
Svetoslava798c0a2014-05-15 10:47:19 -07001288 callback = mCallback;
1289 }
1290
1291 // If the callback is null we are destroyed.
1292 if (callback == null) {
1293 Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
1294 + "finish the printing activity before print completion "
1295 + "or did you invoke a callback after finish?");
1296 return;
1297 }
1298
1299 try {
1300 callback.onWriteCanceled(mSequence);
1301 } catch (RemoteException re) {
1302 Log.e(LOG_TAG, "Error calling onWriteCanceled", re);
1303 } finally {
1304 destroy();
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001305 }
1306 }
1307
Svetoslava798c0a2014-05-15 10:47:19 -07001308 @Override
1309 public void destroy() {
1310 synchronized (mLock) {
1311 IoUtils.closeQuietly(mFd);
1312 mCallback = null;
1313 mFd = null;
1314 mPendingCallback = null;
1315 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -07001316 }
1317 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001318 }
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001319
Philip P. Moltmann76d7e3e2016-01-15 13:22:13 -08001320 /**
1321 * @hide
1322 */
1323 public static final class PrintJobStateChangeListenerWrapper extends
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001324 IPrintJobStateChangeListener.Stub {
1325 private final WeakReference<PrintJobStateChangeListener> mWeakListener;
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001326 private final WeakReference<Handler> mWeakHandler;
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001327
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001328 public PrintJobStateChangeListenerWrapper(PrintJobStateChangeListener listener,
1329 Handler handler) {
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001330 mWeakListener = new WeakReference<PrintJobStateChangeListener>(listener);
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001331 mWeakHandler = new WeakReference<Handler>(handler);
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001332 }
1333
1334 @Override
1335 public void onPrintJobStateChanged(PrintJobId printJobId) {
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001336 Handler handler = mWeakHandler.get();
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001337 PrintJobStateChangeListener listener = mWeakListener.get();
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001338 if (handler != null && listener != null) {
1339 SomeArgs args = SomeArgs.obtain();
Svetoslav Ganovd91cb3e2013-10-12 15:44:42 -07001340 args.arg1 = this;
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001341 args.arg2 = printJobId;
1342 handler.obtainMessage(MSG_NOTIFY_PRINT_JOB_STATE_CHANGED,
1343 args).sendToTarget();
Svetoslav Ganov704697b2013-09-21 20:30:24 -07001344 }
1345 }
Svetoslav Ganova18661d2013-10-09 22:55:49 -07001346
Svetoslav Ganovd91cb3e2013-10-12 15:44:42 -07001347 public void destroy() {
1348 mWeakListener.clear();
1349 }
1350
1351 public PrintJobStateChangeListener getListener() {
1352 return mWeakListener.get();
1353 }
1354 }
Philip P. Moltmann66c96592016-02-24 11:32:43 -08001355
1356 /**
1357 * @hide
1358 */
1359 public static final class PrintServicesChangeListenerWrapper extends
1360 IPrintServicesChangeListener.Stub {
1361 private final WeakReference<PrintServicesChangeListener> mWeakListener;
1362 private final WeakReference<Handler> mWeakHandler;
1363
1364 public PrintServicesChangeListenerWrapper(PrintServicesChangeListener listener,
1365 Handler handler) {
1366 mWeakListener = new WeakReference<>(listener);
1367 mWeakHandler = new WeakReference<>(handler);
1368 }
1369
1370 @Override
1371 public void onPrintServicesChanged() {
1372 Handler handler = mWeakHandler.get();
1373 PrintServicesChangeListener listener = mWeakListener.get();
1374 if (handler != null && listener != null) {
Philip P. Moltmann7e018952017-04-02 14:34:09 -07001375 handler.post(listener::onPrintServicesChanged);
Philip P. Moltmann66c96592016-02-24 11:32:43 -08001376 }
1377 }
1378
1379 public void destroy() {
1380 mWeakListener.clear();
1381 }
Philip P. Moltmann66c96592016-02-24 11:32:43 -08001382 }
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -07001383
1384 /**
1385 * @hide
1386 */
1387 public static final class PrintServiceRecommendationsChangeListenerWrapper extends
1388 IRecommendationsChangeListener.Stub {
1389 private final WeakReference<PrintServiceRecommendationsChangeListener> mWeakListener;
1390 private final WeakReference<Handler> mWeakHandler;
1391
1392 public PrintServiceRecommendationsChangeListenerWrapper(
1393 PrintServiceRecommendationsChangeListener listener, Handler handler) {
1394 mWeakListener = new WeakReference<>(listener);
1395 mWeakHandler = new WeakReference<>(handler);
1396 }
1397
1398 @Override
1399 public void onRecommendationsChanged() {
1400 Handler handler = mWeakHandler.get();
1401 PrintServiceRecommendationsChangeListener listener = mWeakListener.get();
1402 if (handler != null && listener != null) {
Philip P. Moltmann7e018952017-04-02 14:34:09 -07001403 handler.post(listener::onPrintServiceRecommendationsChanged);
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -07001404 }
1405 }
1406
1407 public void destroy() {
1408 mWeakListener.clear();
1409 }
Philip P. Moltmann9dcb86a2016-03-14 14:31:12 -07001410 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001411}