1/*
2 * Copyright (c) 2022 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 ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility';
16import common from '@ohos.app.ability.common';
17import window from '@ohos.window';
18import inputMethod from '@ohos.inputMethod';
19import commonEvent from '@ohos.commonEventManager';
20import Want from '@ohos.app.ability.Want';
21import display from '@ohos.display';
22import { BusinessError } from '@ohos.base';
23
24let TAG: string = '[InputMethodChooseDialog]';
25let PACKAGE_ADDED: string = 'usual.event.PACKAGE_ADDED';
26let PACKAGE_REMOVED: string = 'usual.event.PACKAGE_REMOVED';
27let subscribeInfo: commonEvent.CommonEventSubscribeInfo = {
28  events: [PACKAGE_ADDED, PACKAGE_REMOVED]
29};
30
31interface DialogRect {
32  left: number;
33  top: number;
34  width: number;
35  height: number;
36}
37
38const DISPLAY_SCALE: number = 0.35;
39const MIN_SIZE: number = 350;
40const MAX_SZIE: number = 550;
41const DIALOG_POSITION_X: number = 50;
42const DIALOG_POSITION_Y_SCALE: number = 0.5;
43
44export default class ServiceExtAbility extends ServiceExtensionAbility {
45  private extensionWin: window.Window | undefined = undefined;
46  private mContext: common.ServiceExtensionContext | undefined = undefined;
47  private windowNum: number = 0;
48
49  onCreate(want: Want): void {
50    console.log(TAG, 'onCreate');
51    this.windowNum = 0;
52    this.mContext = this.context;
53  }
54
55  onRequest(want: Want, startId: number): void {
56    console.log(TAG, 'onRequest execute');
57    let defaultDisplay = display.getDefaultDisplaySync();
58    let size = defaultDisplay.width * DISPLAY_SCALE > MIN_SIZE ? defaultDisplay.width * DISPLAY_SCALE : MIN_SIZE;
59    size = size < MAX_SZIE ? size : MAX_SZIE;
60    let dialogTop = defaultDisplay.height * DIALOG_POSITION_Y_SCALE;
61    let dialogRect: DialogRect = {
62      left: DIALOG_POSITION_X,
63      top: dialogTop,
64      width: size,
65      height: size
66    };
67    let windowConfig: window.Configuration = {
68      name: 'inputmethod Dialog',
69      windowType: window.WindowType.TYPE_FLOAT,
70      ctx: this.mContext
71    };
72    this.getInputMethods().then(() => {
73      this.createWindow(windowConfig, dialogRect);
74    });
75
76    commonEvent.createSubscriber(subscribeInfo, (error: BusinessError, subcriber: commonEvent.CommonEventSubscriber) => {
77      commonEvent.subscribe(subcriber, (error: BusinessError, commonEventData: commonEvent.CommonEventData) => {
78        if (error) {
79          console.log(TAG + `commonEvent subscribe error, errorCode: ${error}`);
80          return;
81        }
82        console.log(TAG + 'commonEvent:' + JSON.stringify(commonEventData?.event));
83        if (commonEventData?.event === PACKAGE_ADDED || commonEventData?.event === PACKAGE_REMOVED) {
84          this.updateImeList();
85        }
86      });
87    });
88  }
89
90  onDestroy(): void {
91    console.log(TAG + 'ServiceExtAbility destroyed');
92    this.releaseContext();
93  }
94
95  private async createWindow(config: window.Configuration, rect: DialogRect): Promise<void> {
96    console.log(TAG + 'createWindow execute');
97    try {
98      if (this.windowNum > 0) {
99        this.updateImeList();
100        return;
101      }
102      try {
103        this.extensionWin = await window.createWindow(config);
104        console.info(TAG + 'Succeeded in creating the window. Data: ' + JSON.stringify(this.extensionWin));
105        this.extensionWin.on('windowEvent', async (data: window.WindowEventType) => {
106          console.log(TAG + 'windowEvent:' + JSON.stringify(data));
107          if (data === window.WindowEventType.WINDOW_INACTIVE) {
108            await this.releaseContext();
109          }
110        });
111        await this.extensionWin.moveWindowTo(rect.left, rect.top);
112        await this.extensionWin.resize(rect.width, rect.height);
113        await this.extensionWin.setUIContent('pages/index');
114        await this.extensionWin.showWindow();
115        this.windowNum++;
116        console.log(TAG + 'window create successfully');
117      } catch (exception) {
118        console.error('Failed to create the window. Cause: ' + JSON.stringify(exception));
119      }
120    } catch {
121      console.info(TAG + 'window create failed');
122    }
123  }
124
125  private async getInputMethods(): Promise<void> {
126    let inputMethodList: Array<inputMethod.InputMethodProperty> = [];
127    try {
128      let enableList = await inputMethod.getSetting().getInputMethods(true);
129      let disableList = await inputMethod.getSetting().getInputMethods(false);
130      inputMethodList = enableList.concat(disableList);
131      AppStorage.setOrCreate('inputMethodList', inputMethodList);
132    } catch {
133      console.log(TAG + 'getInputMethods failed');
134    }
135  }
136
137  private async updateImeList(): Promise<void> {
138    await this.getInputMethods().then(async () => {
139      if (this.extensionWin) {
140        await this.extensionWin.setUIContent('pages/index');
141        if (!this.extensionWin.isWindowShowing()) {
142          await this.extensionWin.showWindow();
143        }
144      }
145    });
146  }
147
148  public async releaseContext(): Promise<void> {
149    await this.extensionWin?.destroyWindow();
150    await this.mContext?.terminateSelf();
151  }
152};