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 */
15
16import { configMgr, GlobalObject, GlobalThisHelper, GlobalThisStorageKey, MediaSizeHelper } from '@ohos/common';
17import AppStorageHelper from '../Common/Adapter/AppStorageHelper';
18import { Log } from '@ohos/common';
19import { Constants, AppStorageKeyName, PreferencesKey } from '@ohos/common';
20import type common from '@ohos.app.ability.common';
21import PreferencesAdapter from '../Common/Adapter/PreferencesAdapter';
22import bundleManager from '@ohos.bundle.bundleManager';
23import PrintAdapter from '../Common/Adapter/PrintAdapter';
24import { Configuration } from '@ohos.app.ability.Configuration';
25import image from '@ohos.multimedia.image';
26import Want from '@ohos.app.ability.Want';
27import UIExtensionAbility from '@ohos.app.ability.UIExtensionAbility';
28import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession';
29import CheckEmptyUtils from '@ohos/common';
30import Queue from '@ohos.util.Queue';
31import Util from '../Common/Utils/Util';
32import type { PrintAttributes } from '@ohos/common';
33
34const TAG = '[MainAbility]:';
35
36export default class MainAbility extends UIExtensionAbility {
37  private lock: boolean = false;
38  private storeRequest = new Queue<RequestDialogModel>();
39  private readonly PRIVACY_STATEMENT_STORE: string = 'privacyStatementStore';
40  private session: UIExtensionContentSession | undefined = undefined;
41
42  onCreate(): void {
43    GlobalThisHelper.createValue<common.UIExtensionContext>(this.context, GlobalThisStorageKey.KEY_MAIN_ABILITY_CONTEXT, true);
44    MediaSizeHelper.init(this.context);
45    this.context.resourceManager.getConfiguration((error, value) => {
46      AppStorageHelper.createValue<string>(value.locale as string, AppStorageKeyName.CONFIG_LANGUAGE);
47    });
48
49    bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT)
50      .then((bundleInfo) => {
51        AppStorageHelper.createValue<string>(bundleInfo.versionName as string, AppStorageKeyName.APP_VERSION);
52      });
53    this.subscribe();
54  }
55
56  onDestroy() {
57    // Main window is destroyed, release UI related resources
58    Log.info(TAG, 'onDestroy');
59    let adapter = GlobalThisHelper.getValue<PrintAdapter>(GlobalThisStorageKey.KEY_PRINT_ADAPTER);
60    let previewPageInstances: number = AppStorageHelper.getValue(AppStorageKeyName.PREVIEW_PAGE_INSTANCE) as number;
61    if (previewPageInstances === 1) {
62      adapter?.getPrinterDiscCtl()?.stopDiscovery('');
63      this.unsubscribe();
64    }
65    AppStorageHelper.createValue<number>(previewPageInstances - 1, AppStorageKeyName.PREVIEW_PAGE_INSTANCE);
66    let pixelMap = GlobalThisHelper.getValue<image.PixelMap>(GlobalThisStorageKey.KEY_CURRENT_PIXELMAP);
67    if (pixelMap !== undefined) {
68      pixelMap.release().then(() => {
69        Log.info(TAG, 'onDestroy currentPixelMap release success');
70      });
71    }
72    GlobalObject.getInstance().removeObject(Constants.PREVIEW_IS_VERTICAL);
73    GlobalObject.getInstance().removeObject(Constants.PREVIEW_DIALOG_COUNT);
74    let res = AppStorageHelper.deleteValue(AppStorageKeyName.imageSourcesName);
75    Log.info(TAG, 'delete imageSources:' + res);
76  }
77
78  onSessionCreate(want: Want, session: UIExtensionContentSession): void {
79    Log.info(TAG + 'onSessionCreate, want: ' + JSON.stringify(want));
80    this.session = session;
81    let fileList: string[] = [];
82    let callerPid: string = '';
83    let pkgName: string = '';
84    let docName: string = '';
85    let attributes: string = '';
86    let jobId: string = '';
87    if (want.parameters !== undefined) {
88      fileList = want.parameters[Constants.WANT_FILE_LIST_KEY] as string[];
89      Log.info(TAG + 'fileList: ' + JSON.stringify(fileList));
90
91      callerPid = want.parameters[Constants.WANT_CALLERPID_KEY] as string;
92      pkgName = want.parameters[Constants.WANT_PKG_NAME_KEY] as string;
93      docName = want.parameters[Constants.WANT_DOCUMENT_NAME_KEY] as string;
94      attributes = want.parameters[Constants.wantPrintAttributeKey] as string;
95      jobId = want.parameters[Constants.WANT_JOB_ID_KEY] as string;
96    }
97    let printAttributes: PrintAttributes | undefined = undefined;
98    if (!CheckEmptyUtils.checkStrIsEmpty(attributes)) {
99      printAttributes = JSON.parse(attributes);
100    }
101    this.storeRequest.add(new RequestDialogModel(fileList, callerPid, pkgName, docName, printAttributes, jobId));
102    this.consumeRequest();
103  }
104
105  private consumeRequest(): void {
106    if (this.storeRequest.length === 0) {
107      Log.warn(TAG, 'no dialogRequest in queue.');
108      return;
109    }
110    if (this.lock) {
111      Log.warn(TAG, 'consumer is busying: need waiting');
112      return;
113    }
114    this.handleDialogRequest(this.storeRequest.pop(), this.requestCompleteCallback);
115  }
116
117  private async handleDialogRequest(model: RequestDialogModel, callback: (id: string) => void): Promise<void> {
118    GlobalThisHelper.createValue<string>(model.jobId, GlobalThisStorageKey.KEY_JOB_ID);
119    let dialogId = Util.getPreviewDialogId(model.jobId);
120    let pkgNameMap: Map<string, string> = AppStorageHelper.getValue<Map<string, string>>(
121      AppStorageKeyName.INGRESS_PACKAGE) ?? new Map<string, string>();
122    let docNameMap: Map<string, string> = AppStorageHelper.getValue<Map<string, string>>(
123      AppStorageKeyName.DOCUMENT_NAME) ?? new Map<string, string>();
124    let previewPageInstances: number = AppStorageHelper.getValue(AppStorageKeyName.PREVIEW_PAGE_INSTANCE) ?? 0;
125    pkgNameMap.set(dialogId, model.pkgName);
126    docNameMap.set(dialogId, model.docName);
127    AppStorageHelper.createValue<Map<string, string>>(pkgNameMap, AppStorageKeyName.INGRESS_PACKAGE);
128    AppStorageHelper.createValue<Map<string, string>>(docNameMap, AppStorageKeyName.DOCUMENT_NAME);
129    let storage: LocalStorage = new LocalStorage();
130    storage.setOrCreate<string>(Constants.WANT_JOB_ID_KEY, model.jobId);
131    storage.setOrCreate(Constants.WANT_FILE_LIST_KEY, model.fileList ?? new Array<string>());
132    storage.setOrCreate(Constants.wantPrintAttributeKey, model.printAttributes);
133    storage.setOrCreate(Constants.WINDOW_ID, dialogId);
134    storage.setOrCreate(Constants.SESSION, this.session);
135
136    let pageUrl = Constants.PRINT_PREVIEW_PAGE_URL;
137    await this.isFirstUsePrint().then((flag) => {
138      Log.info(TAG, 'isFirstUsePrint : ' + flag);
139      if (flag) {
140        pageUrl = Constants.PRIVACY_STATEMENT_PAGE_URL;
141      } else {
142        pageUrl = Constants.PRINT_PREVIEW_PAGE_URL;
143      }
144    });
145    GlobalObject.getInstance().setObject(Constants.PREVIEW_DIALOG_COUNT,
146      (GlobalObject.getInstance().getObject(Constants.PREVIEW_DIALOG_COUNT) as number) + 1);
147
148    this.session?.loadContent(pageUrl, storage);
149    this.session?.setWindowBackgroundColor('#40000000');
150    AppStorageHelper.createValue<number>(previewPageInstances + 1, AppStorageKeyName.PREVIEW_PAGE_INSTANCE);
151    callback(dialogId);
152    Log.debug(TAG, 'handleDialogRequest end.');
153  }
154
155  private requestCompleteCallback = (dialogId: string): void => {
156    Log.info(TAG, `requestCompleteCallback CreateWindow:${JSON.stringify(dialogId)} completed`);
157    this.lock = false;
158    this.consumeRequest();
159  };
160
161  onConfigurationUpdated(config: Configuration): void {
162    Log.info(TAG, 'onConfigurationUpdated, language:' + config.language);
163    AppStorageHelper.createValue<string>(config.language as string, AppStorageKeyName.CONFIG_LANGUAGE);
164  }
165
166  onForeground() {
167    // Ability has brought to foreground
168    Log.info(TAG, 'onForeground');
169  }
170
171  onBackground() {
172    // Ability has back to background
173    Log.info(TAG, 'onBackground');
174  }
175
176  async isFirstUsePrint(): Promise<boolean> {
177    Log.info(TAG, 'isFirstUsePrint start');
178    const success = await PreferencesAdapter.getInstance().getOrCreatePreferencesSync(this.PRIVACY_STATEMENT_STORE);
179    Log.info(TAG, 'isFirstUsePrint getOrCreatePreferencesSync success: ' + success);
180    if (success) {
181      const agreePrivacyStatement = await PreferencesAdapter.getInstance()
182        .getValue(PreferencesKey.KEY_PRIVACY_STATEMENT_PREFERENCES);
183      Log.info(TAG, 'isFirstUsePrint getValue agreePrivacyStatement: ' + agreePrivacyStatement);
184      if (agreePrivacyStatement) {
185        return false;
186      } else {
187        return true;
188      }
189    } else {
190      Log.info(TAG, 'isFirstUsePrint success is not');
191      return true;
192    }
193  }
194
195  private subscribe(): void {
196    configMgr.onStart(this.context);
197  }
198
199  private unsubscribe(): void {
200    configMgr.onStop();
201  }
202};
203
204class RequestDialogModel {
205  fileList: Array<string>;
206  callerPid: string;
207  pkgName: string;
208  docName: string;
209  printAttributes: PrintAttributes | undefined;
210  jobId: string;
211
212  constructor(fileList: Array<string>, callerPid: string, pkgName: string, docName: string,
213              printAttributes: PrintAttributes | undefined, jobId: string) {
214    this.fileList = fileList;
215    this.callerPid = callerPid;
216    this.pkgName = pkgName;
217    this.docName = docName;
218    this.printAttributes = printAttributes;
219    this.jobId = jobId;
220  }
221}
222