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