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 17 package android.printservice; 18 19 import android.os.RemoteException; 20 import android.print.PrintJobId; 21 import android.print.PrintJobInfo; 22 import android.text.TextUtils; 23 import android.util.Log; 24 25 /** 26 * This class represents a print job from the perspective of a print 27 * service. It provides APIs for observing the print job state and 28 * performing operations on the print job. 29 * <p> 30 * <strong>Note: </strong> All methods of this class must be invoked on 31 * the main application thread. 32 * </p> 33 */ 34 public final class PrintJob { 35 36 private static final String LOG_TAG = "PrintJob"; 37 38 private final IPrintServiceClient mPrintServiceClient; 39 40 private final PrintDocument mDocument; 41 42 private PrintJobInfo mCachedInfo; 43 PrintJob(PrintJobInfo jobInfo, IPrintServiceClient client)44 PrintJob(PrintJobInfo jobInfo, IPrintServiceClient client) { 45 mCachedInfo = jobInfo; 46 mPrintServiceClient = client; 47 mDocument = new PrintDocument(mCachedInfo.getId(), client, 48 jobInfo.getDocumentInfo()); 49 } 50 51 /** 52 * Gets the unique print job id. 53 * 54 * @return The id. 55 */ getId()56 public PrintJobId getId() { 57 PrintService.throwIfNotCalledOnMainThread(); 58 return mCachedInfo.getId(); 59 } 60 61 /** 62 * Gets the {@link PrintJobInfo} that describes this job. 63 * <p> 64 * <strong>Node:</strong>The returned info object is a snapshot of the 65 * current print job state. Every call to this method returns a fresh 66 * info object that reflects the current print job state. 67 * </p> 68 * 69 * @return The print job info. 70 */ getInfo()71 public PrintJobInfo getInfo() { 72 PrintService.throwIfNotCalledOnMainThread(); 73 if (isInImmutableState()) { 74 return mCachedInfo; 75 } 76 PrintJobInfo info = null; 77 try { 78 info = mPrintServiceClient.getPrintJobInfo(mCachedInfo.getId()); 79 } catch (RemoteException re) { 80 Log.e(LOG_TAG, "Couldn't get info for job: " + mCachedInfo.getId(), re); 81 } 82 if (info != null) { 83 mCachedInfo = info; 84 } 85 return mCachedInfo; 86 } 87 88 /** 89 * Gets the printed document. 90 * 91 * @return The document. 92 */ getDocument()93 public PrintDocument getDocument() { 94 PrintService.throwIfNotCalledOnMainThread(); 95 return mDocument; 96 } 97 98 /** 99 * Gets whether this print job is queued. Such a print job is 100 * ready to be printed and can be started or cancelled. 101 * 102 * @return Whether the print job is queued. 103 * 104 * @see #start() 105 * @see #cancel() 106 */ isQueued()107 public boolean isQueued() { 108 PrintService.throwIfNotCalledOnMainThread(); 109 return getInfo().getState() == PrintJobInfo.STATE_QUEUED; 110 } 111 112 /** 113 * Gets whether this print job is started. Such a print job is 114 * being printed and can be completed or canceled or failed. 115 * 116 * @return Whether the print job is started. 117 * 118 * @see #complete() 119 * @see #cancel() 120 * @see #fail(CharSequence) 121 */ isStarted()122 public boolean isStarted() { 123 PrintService.throwIfNotCalledOnMainThread(); 124 return getInfo().getState() == PrintJobInfo.STATE_STARTED; 125 } 126 127 /** 128 * Gets whether this print job is blocked. Such a print job is halted 129 * due to an abnormal condition and can be started or canceled or failed. 130 * 131 * @return Whether the print job is blocked. 132 * 133 * @see #start() 134 * @see #cancel() 135 * @see #fail(CharSequence) 136 */ isBlocked()137 public boolean isBlocked() { 138 PrintService.throwIfNotCalledOnMainThread(); 139 return getInfo().getState() == PrintJobInfo.STATE_BLOCKED; 140 } 141 142 /** 143 * Gets whether this print job is completed. Such a print job 144 * is successfully printed. This is a final state. 145 * 146 * @return Whether the print job is completed. 147 * 148 * @see #complete() 149 */ isCompleted()150 public boolean isCompleted() { 151 PrintService.throwIfNotCalledOnMainThread(); 152 return getInfo().getState() == PrintJobInfo.STATE_COMPLETED; 153 } 154 155 /** 156 * Gets whether this print job is failed. Such a print job is 157 * not successfully printed due to an error. This is a final state. 158 * 159 * @return Whether the print job is failed. 160 * 161 * @see #fail(CharSequence) 162 */ isFailed()163 public boolean isFailed() { 164 PrintService.throwIfNotCalledOnMainThread(); 165 return getInfo().getState() == PrintJobInfo.STATE_FAILED; 166 } 167 168 /** 169 * Gets whether this print job is cancelled. Such a print job was 170 * cancelled as a result of a user request. This is a final state. 171 * 172 * @return Whether the print job is cancelled. 173 * 174 * @see #cancel() 175 */ isCancelled()176 public boolean isCancelled() { 177 PrintService.throwIfNotCalledOnMainThread(); 178 return getInfo().getState() == PrintJobInfo.STATE_CANCELED; 179 } 180 181 /** 182 * Starts the print job. You should call this method if {@link 183 * #isQueued()} or {@link #isBlocked()} returns true and you started 184 * resumed printing. 185 * 186 * @return Whether the job was started. 187 * 188 * @see #isQueued() 189 * @see #isBlocked() 190 */ start()191 public boolean start() { 192 PrintService.throwIfNotCalledOnMainThread(); 193 final int state = getInfo().getState(); 194 if (state == PrintJobInfo.STATE_QUEUED 195 || state == PrintJobInfo.STATE_BLOCKED) { 196 return setState(PrintJobInfo.STATE_STARTED, null); 197 } 198 return false; 199 } 200 201 /** 202 * Blocks the print job. You should call this method if {@link 203 * #isStarted()} or {@link #isBlocked()} returns true and you need 204 * to block the print job. For example, the user has to add some 205 * paper to continue printing. To resume the print job call {@link 206 * #start()}. 207 * 208 * @return Whether the job was blocked. 209 * 210 * @see #isStarted() 211 * @see #isBlocked() 212 */ block(String reason)213 public boolean block(String reason) { 214 PrintService.throwIfNotCalledOnMainThread(); 215 PrintJobInfo info = getInfo(); 216 final int state = info.getState(); 217 if (state == PrintJobInfo.STATE_STARTED 218 || (state == PrintJobInfo.STATE_BLOCKED 219 && !TextUtils.equals(info.getStateReason(), reason))) { 220 return setState(PrintJobInfo.STATE_BLOCKED, reason); 221 } 222 return false; 223 } 224 225 /** 226 * Completes the print job. You should call this method if {@link 227 * #isStarted()} returns true and you are done printing. 228 * 229 * @return Whether the job as completed. 230 * 231 * @see #isStarted() 232 */ complete()233 public boolean complete() { 234 PrintService.throwIfNotCalledOnMainThread(); 235 if (isStarted()) { 236 return setState(PrintJobInfo.STATE_COMPLETED, null); 237 } 238 return false; 239 } 240 241 /** 242 * Fails the print job. You should call this method if {@link 243 * #isQueued()} or {@link #isStarted()} or {@link #isBlocked()} 244 * returns true you failed while printing. 245 * 246 * @param error The human readable, short, and translated reason 247 * for the failure. 248 * @return Whether the job was failed. 249 * 250 * @see #isQueued() 251 * @see #isStarted() 252 * @see #isBlocked() 253 */ fail(String error)254 public boolean fail(String error) { 255 PrintService.throwIfNotCalledOnMainThread(); 256 if (!isInImmutableState()) { 257 return setState(PrintJobInfo.STATE_FAILED, error); 258 } 259 return false; 260 } 261 262 /** 263 * Cancels the print job. You should call this method if {@link 264 * #isQueued()} or {@link #isStarted() or #isBlocked()} returns 265 * true and you canceled the print job as a response to a call to 266 * {@link PrintService#onRequestCancelPrintJob(PrintJob)}. 267 * 268 * @return Whether the job is canceled. 269 * 270 * @see #isStarted() 271 * @see #isQueued() 272 * @see #isBlocked() 273 */ cancel()274 public boolean cancel() { 275 PrintService.throwIfNotCalledOnMainThread(); 276 if (!isInImmutableState()) { 277 return setState(PrintJobInfo.STATE_CANCELED, null); 278 } 279 return false; 280 } 281 282 /** 283 * Sets a tag that is valid in the context of a {@link PrintService} 284 * and is not interpreted by the system. For example, a print service 285 * may set as a tag the key of the print job returned by a remote 286 * print server, if the printing is off handed to a cloud based service. 287 * 288 * @param tag The tag. 289 * @return True if the tag was set, false otherwise. 290 */ setTag(String tag)291 public boolean setTag(String tag) { 292 PrintService.throwIfNotCalledOnMainThread(); 293 if (isInImmutableState()) { 294 return false; 295 } 296 try { 297 return mPrintServiceClient.setPrintJobTag(mCachedInfo.getId(), tag); 298 } catch (RemoteException re) { 299 Log.e(LOG_TAG, "Error setting tag for job: " + mCachedInfo.getId(), re); 300 } 301 return false; 302 } 303 304 /** 305 * Gets the print job tag. 306 * 307 * @return The tag or null. 308 * 309 * @see #setTag(String) 310 */ getTag()311 public String getTag() { 312 PrintService.throwIfNotCalledOnMainThread(); 313 return getInfo().getTag(); 314 } 315 316 /** 317 * Gets the value of an advanced (printer specific) print option. 318 * 319 * @param key The option key. 320 * @return The option value. 321 */ getAdvancedStringOption(String key)322 public String getAdvancedStringOption(String key) { 323 PrintService.throwIfNotCalledOnMainThread(); 324 return getInfo().getAdvancedStringOption(key); 325 } 326 327 /** 328 * Gets whether this job has a given advanced (printer specific) print 329 * option. 330 * 331 * @param key The option key. 332 * @return Whether the option is present. 333 */ hasAdvancedOption(String key)334 public boolean hasAdvancedOption(String key) { 335 PrintService.throwIfNotCalledOnMainThread(); 336 return getInfo().hasAdvancedOption(key); 337 } 338 339 /** 340 * Gets the value of an advanced (printer specific) print option. 341 * 342 * @param key The option key. 343 * @return The option value. 344 */ getAdvancedIntOption(String key)345 public int getAdvancedIntOption(String key) { 346 PrintService.throwIfNotCalledOnMainThread(); 347 return getInfo().getAdvancedIntOption(key); 348 } 349 350 @Override equals(Object obj)351 public boolean equals(Object obj) { 352 if (this == obj) { 353 return true; 354 } 355 if (obj == null) { 356 return false; 357 } 358 if (getClass() != obj.getClass()) { 359 return false; 360 } 361 PrintJob other = (PrintJob) obj; 362 return (mCachedInfo.getId().equals(other.mCachedInfo.getId())); 363 } 364 365 @Override hashCode()366 public int hashCode() { 367 return mCachedInfo.getId().hashCode(); 368 } 369 isInImmutableState()370 private boolean isInImmutableState() { 371 final int state = mCachedInfo.getState(); 372 return state == PrintJobInfo.STATE_COMPLETED 373 || state == PrintJobInfo.STATE_CANCELED 374 || state == PrintJobInfo.STATE_FAILED; 375 } 376 setState(int state, String error)377 private boolean setState(int state, String error) { 378 try { 379 if (mPrintServiceClient.setPrintJobState(mCachedInfo.getId(), state, error)) { 380 // Best effort - update the state of the cached info since 381 // we may not be able to re-fetch it later if the job gets 382 // removed from the spooler as a result of the state change. 383 mCachedInfo.setState(state); 384 mCachedInfo.setStateReason(error); 385 return true; 386 } 387 } catch (RemoteException re) { 388 Log.e(LOG_TAG, "Error setting the state of job: " + mCachedInfo.getId(), re); 389 } 390 return false; 391 } 392 } 393