1/* 2 * Copyright (c) 2023-2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15import CheckEmptyUtils, { Constants, convertToSpoolerPrintJob, isValidPrintJob, Log, PrintJob, PrintJobChangeListener, queryAllPrintJobs, SingletonHelper } from '@ohos/common'; 16import print from '@ohos.print'; 17import FileUtil from '../Common/Utils/FileUtil'; 18 19const TAG: string = 'PrintJobManager'; 20class PrintJobManager { 21 private localJobMap: Map<string, PrintJob> = new Map(); 22 private isStarted: boolean = false; 23 private isRegister: boolean = false; 24 private printJobChangeListeners: Map<string, PrintJobChangeListener> = new Map(); 25 26 private onJobStateChanged = (jobState: print.PrintJobState, job: print.PrintJob): void => { 27 Log.info(TAG, `onJobStateChanged jobId:${job?.jobId}, jobState:${jobState}, jobSubstate:${job?.jobSubstate}`); 28 if (!isValidPrintJob(job)) { 29 Log.warn(TAG, 'onJobStateChanged invalid job.'); 30 return; 31 } 32 let printJob: PrintJob = convertToSpoolerPrintJob(job); 33 this.deleteLocalSource(printJob); 34 this.updatePrintJob(printJob); 35 }; 36 37 onStart(printerId?: string): void { 38 if (this.isStarted) { 39 return; 40 } 41 this.isStarted = true; 42 this.registerCallback(); 43 } 44 45 onStop(): void { 46 this.unregisterCallback(); 47 this.printJobChangeListeners?.clear(); 48 } 49 50 registerCallback(): void { 51 Log.info(TAG, `registerCallback isRegister:${this.isRegister}`); 52 if (!this.isRegister) { 53 print.on(`jobStateChange`, this.onJobStateChanged); 54 this.isRegister = true; 55 } 56 } 57 unregisterCallback(): void { 58 Log.info(TAG, `unregisterCallback isRegister:${this.isRegister}`); 59 if (this.isRegister) { 60 print.off('jobStateChange', (data: boolean) => { 61 Log.info(TAG, `off jobStateChange data:` + JSON.stringify(data)); 62 }); 63 this.isRegister = false; 64 } 65 } 66 67 registerJobChangeListener(key: string, listener: PrintJobChangeListener): void { 68 Log.info(TAG, `registerJobChangeListener key:${key}`); 69 if (key === null || listener === null) { 70 return; 71 } 72 this.printJobChangeListeners?.set(key, listener); 73 } 74 75 unregisterJobChangeListener(key: string): void { 76 if (key === null) { 77 return; 78 } 79 this.printJobChangeListeners?.delete(key); 80 } 81 82 async getPrintJobQueue(printerId?: string): Promise<void> { 83 let remoteJobQueue: Array<PrintJob> = await Promise.resolve(queryAllPrintJobs(printerId)); 84 let sortedJobQueue: Array<PrintJob> = await Promise.resolve(this.sortPrintJobs(remoteJobQueue)); 85 await Promise.resolve(this.updateLocalPrintJobQueue(sortedJobQueue)); 86 } 87 88 private updateLocalPrintJobQueue(remoteJobQueue?: Array<PrintJob>): void { 89 Log.info(TAG, `updateLocalPrintJobQueue remoteJobQueue.length:${remoteJobQueue.length},localJobMap.length: ${this.localJobMap.size}`) 90 if (remoteJobQueue.length === 0) { 91 this.localJobMap.clear(); 92 this.notifyAllPrintJobsFinished(); 93 Log.debug(TAG, 'updateLocalPrintJobQueue notifyAllPrintJobsFinished'); 94 } else { 95 for (let item of remoteJobQueue) { 96 this.localJobMap.set(item.jobId, item); 97 this.printJobChangeListeners?.forEach((listener, key) => { 98 Log.info(TAG, `updateLocalPrintJobQueue listener.onAddPrintJob:Key:${key}, jobId:${item.jobId}`); 99 listener?.onAddPrintJob(item, this.checkBlockInQueue()); 100 }); 101 } 102 } 103 } 104 105 cancelPrintJob(jobId: string): void { 106 Log.info(TAG, 'cancelPrintJob enter.'); 107 let job = this.localJobMap.get(jobId); 108 if (job) { 109 Log.info(TAG, `cancelPrintJob jobId:${jobId}`); 110 print.cancelPrintJob(jobId).then((data)=> { 111 Log.info(TAG, 'cancelPrintJob success, data:' + JSON.stringify(data)); 112 }).catch((err) =>{ 113 Log.info(TAG, 'cancelPrintJob failed, err:' + JSON.stringify(err)); 114 }); 115 } 116 Log.info(TAG, 'cancelPrintJob end.'); 117 } 118 119 updatePrintJob(job: PrintJob): void { 120 Log.info(TAG, 'updatePrintJob enter.'); 121 if (this.localJobMap.has(job.jobId)) { 122 Log.info(TAG, `updatePrintJob jobId: ${job.jobId}`); 123 this.localJobMap.set(job.jobId, job); 124 this.printJobChangeListeners?.forEach((listener, key) => { 125 Log.info(TAG, `updatePrintJob listener.onUpdatePrintJob: Key:${key}, jobState:${job.jobState}`); 126 listener?.onUpdatePrintJob(job, this.checkBlockInQueue()); 127 }); 128 this.handleCompleteJob(job); 129 } 130 Log.info(TAG, 'updatePrintJob end.'); 131 } 132 133 handleCompleteJob(job: PrintJob): void { 134 Log.info(TAG, 'handleCompleteJob enter.'); 135 if (job.jobState == print.PrintJobState.PRINT_JOB_COMPLETED) { 136 setTimeout(() => { 137 Log.info(TAG, 'handleCompleteJob show completed job time out, jobId=' + job.jobId); 138 this.removePrintJob(job.jobId); 139 }, Constants.SHOW_JOB_COMPLETED_TIMEOUT); 140 } 141 Log.info(TAG, `handleCompleteJob end, jobId:${job.jobId}`); 142 } 143 144 private removePrintJob(jobId: string): void { 145 Log.info(TAG, `removePrintJob enter, jobId:${jobId}`); 146 if (this.localJobMap.delete(jobId)) { 147 Log.info(TAG, `removePrintJob jobId:${jobId} success.`); 148 this.printJobChangeListeners?.forEach((listener, key) => { 149 Log.info(TAG, `removePrintJob listener.onRemovePrintJob: Key:${key}, jobId:${jobId}`); 150 listener?.onRemovePrintJob(jobId, this.checkBlockInQueue()); 151 }); 152 } 153 154 if (this.localJobMap.size === 0) { 155 Log.debug(TAG, 'removePrintJob notifyAllPrintJobsFinished.'); 156 this.notifyAllPrintJobsFinished(); 157 } 158 Log.info(TAG, `removePrintJob end.`); 159 } 160 161 private deleteLocalSource(job: PrintJob): Promise<void> { 162 if (CheckEmptyUtils.isEmpty(job)) { 163 Log.warn(TAG, 'deleteLocalSource invalid job.'); 164 return; 165 } 166 if (job.jobState === print.PrintJobState.PRINT_JOB_BLOCKED || job.jobState === print.PrintJobState.PRINT_JOB_COMPLETED) { 167 Log.info(TAG, `deleteLocalSource jobId:${job.jobId}`); 168 FileUtil.deleteSource(job.jobFiles); 169 } 170 Log.info(TAG, 'deleteLocalSource end.'); 171 } 172 173 checkBlockInQueue(): number { 174 for(let value of this.localJobMap.values()) { 175 if (value.jobState === print.PrintJobState.PRINT_JOB_BLOCKED) { 176 return value.jobSubstate; 177 } 178 } 179 return Constants.NEGATIVE_1; 180 } 181 182 private sortPrintJobs(jobArray: Array<PrintJob>): Promise<Array<PrintJob>> { 183 return new Promise((resolve) => { 184 jobArray.sort((job1, job2): number => { 185 return Number(job1.jobId) - Number(job2.jobId); 186 }); 187 resolve(jobArray); 188 }); 189 } 190 191 private notifyAllPrintJobsFinished(): void { 192 Log.info(TAG, 'notifyAllPrintJobsFinished enter.'); 193 this.printJobChangeListeners?.forEach((listener, key) => { 194 Log.info(TAG, 'notifyAllPrintJobsFinished listener.onAllPrintJobsFinished.'); 195 listener?.onAllPrintJobsFinished(); 196 }); 197 Log.info(TAG, 'notifyAllPrintJobsFinished end.'); 198 } 199} 200export let printJobMgr = SingletonHelper.getInstance(PrintJobManager, TAG);