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