1c5c2eed7Sopenharmony_ci/* 2c5c2eed7Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd. 3c5c2eed7Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4c5c2eed7Sopenharmony_ci * you may not use this file except in compliance with the License. 5c5c2eed7Sopenharmony_ci * You may obtain a copy of the License at 6c5c2eed7Sopenharmony_ci * 7c5c2eed7Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8c5c2eed7Sopenharmony_ci * 9c5c2eed7Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10c5c2eed7Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11c5c2eed7Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12c5c2eed7Sopenharmony_ci * See the License for the specific language governing permissions and 13c5c2eed7Sopenharmony_ci * limitations under the License. 14c5c2eed7Sopenharmony_ci */ 15c5c2eed7Sopenharmony_ciimport UIExtensionAbility from '@ohos.app.ability.UIExtensionAbility'; 16c5c2eed7Sopenharmony_ciimport window from '@ohos.window'; 17c5c2eed7Sopenharmony_ciimport { Log, PermissionDialogException, setAvoidArea } from '../common/utils/utils'; 18c5c2eed7Sopenharmony_ciimport UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; 19c5c2eed7Sopenharmony_ciimport Want from '@ohos.app.ability.Want'; 20c5c2eed7Sopenharmony_ciimport uiExtensionHost from '@ohos.uiExtensionHost'; 21c5c2eed7Sopenharmony_ciimport display from '@ohos.display'; 22c5c2eed7Sopenharmony_ciimport { BusinessError } from '@ohos.base'; 23c5c2eed7Sopenharmony_ciimport Constants from '../common/utils/constant'; 24c5c2eed7Sopenharmony_ciimport AbilityConstant from '@ohos.app.ability.AbilityConstant'; 25c5c2eed7Sopenharmony_ci 26c5c2eed7Sopenharmony_cilet terminating: boolean = false; 27c5c2eed7Sopenharmony_cilet state: boolean = false; 28c5c2eed7Sopenharmony_ci 29c5c2eed7Sopenharmony_ciexport default class PermissionStateSheetAbility extends UIExtensionAbility { 30c5c2eed7Sopenharmony_ci private subWindowForHost: window.Window | null = null; 31c5c2eed7Sopenharmony_ci private shouldChangeState: boolean = true; 32c5c2eed7Sopenharmony_ci 33c5c2eed7Sopenharmony_ci onCreate(launchParam: AbilityConstant.LaunchParam): void { 34c5c2eed7Sopenharmony_ci Log.info('PermissionStateSheetAbility on create'); 35c5c2eed7Sopenharmony_ci } 36c5c2eed7Sopenharmony_ci 37c5c2eed7Sopenharmony_ci onSessionCreate(want: Want, session: UIExtensionContentSession): void { 38c5c2eed7Sopenharmony_ci Log.info('current state:' + state); 39c5c2eed7Sopenharmony_ci if (state == true) { 40c5c2eed7Sopenharmony_ci this.shouldChangeState = false; 41c5c2eed7Sopenharmony_ci Log.error('window state is already set'); 42c5c2eed7Sopenharmony_ci PermissionDialogException(Constants.ERR_MODAL_ALREADY_EXIST, session); 43c5c2eed7Sopenharmony_ci this.context.terminateSelf(); 44c5c2eed7Sopenharmony_ci return; 45c5c2eed7Sopenharmony_ci } else { 46c5c2eed7Sopenharmony_ci Log.info('set window state to true'); 47c5c2eed7Sopenharmony_ci state = true; 48c5c2eed7Sopenharmony_ci } 49c5c2eed7Sopenharmony_ci 50c5c2eed7Sopenharmony_ci let param: Record<string, Object> = { 51c5c2eed7Sopenharmony_ci 'session': session, 'want': want 52c5c2eed7Sopenharmony_ci }; 53c5c2eed7Sopenharmony_ci 54c5c2eed7Sopenharmony_ci let storage: LocalStorage = new LocalStorage(param); 55c5c2eed7Sopenharmony_ci this.createSubWindow(session, storage).then(() => { 56c5c2eed7Sopenharmony_ci // if met error, do not load window, 57c5c2eed7Sopenharmony_ci // in case of nested error codes passed to applications 58c5c2eed7Sopenharmony_ci if (terminating) { 59c5c2eed7Sopenharmony_ci this.context.terminateSelf(); 60c5c2eed7Sopenharmony_ci return; 61c5c2eed7Sopenharmony_ci } 62c5c2eed7Sopenharmony_ci this.loadContentAndSetColor(storage).then((result: boolean) => { 63c5c2eed7Sopenharmony_ci if (!result) { 64c5c2eed7Sopenharmony_ci return; 65c5c2eed7Sopenharmony_ci } 66c5c2eed7Sopenharmony_ci this.showWindow(); 67c5c2eed7Sopenharmony_ci }) 68c5c2eed7Sopenharmony_ci }) 69c5c2eed7Sopenharmony_ci } 70c5c2eed7Sopenharmony_ci 71c5c2eed7Sopenharmony_ci onSessionDestroy(session: UIExtensionContentSession): void { 72c5c2eed7Sopenharmony_ci Log.info('OnSessionDestroy start'); 73c5c2eed7Sopenharmony_ci try { 74c5c2eed7Sopenharmony_ci Log.info('try to reshow non-secure windows.'); 75c5c2eed7Sopenharmony_ci this.subWindowForHost?.hideNonSystemFloatingWindows(false); 76c5c2eed7Sopenharmony_ci } catch (error) { 77c5c2eed7Sopenharmony_ci Log.error('failed to reshowing the non-secure windows. Cause: ' + JSON.stringify(error)); 78c5c2eed7Sopenharmony_ci } 79c5c2eed7Sopenharmony_ci try { 80c5c2eed7Sopenharmony_ci this.subWindowForHost?.off('windowEvent'); 81c5c2eed7Sopenharmony_ci } catch (exception) { 82c5c2eed7Sopenharmony_ci Log.error('fail to call off window event: ' + JSON.stringify(exception)); 83c5c2eed7Sopenharmony_ci } 84c5c2eed7Sopenharmony_ci this.subWindowForHost?.destroyWindow((err, data) => { 85c5c2eed7Sopenharmony_ci if (err && err.code !== 0) { 86c5c2eed7Sopenharmony_ci Log.info('destroy window:' + err.message + ' ' + JSON.stringify(data)); 87c5c2eed7Sopenharmony_ci } 88c5c2eed7Sopenharmony_ci }); 89c5c2eed7Sopenharmony_ci Log.info('destroy window end'); 90c5c2eed7Sopenharmony_ci } 91c5c2eed7Sopenharmony_ci 92c5c2eed7Sopenharmony_ci onForeground(): void { 93c5c2eed7Sopenharmony_ci Log.info('onForegorund'); 94c5c2eed7Sopenharmony_ci this.subWindowForHost?.showWindow(); 95c5c2eed7Sopenharmony_ci } 96c5c2eed7Sopenharmony_ci 97c5c2eed7Sopenharmony_ci onBackground(): void { 98c5c2eed7Sopenharmony_ci Log.info('extension onBackground'); 99c5c2eed7Sopenharmony_ci this.subWindowForHost?.hide(); 100c5c2eed7Sopenharmony_ci } 101c5c2eed7Sopenharmony_ci 102c5c2eed7Sopenharmony_ci onDestroy(): void | Promise<void> { 103c5c2eed7Sopenharmony_ci Log.info('onDestroy'); 104c5c2eed7Sopenharmony_ci if (this.shouldChangeState == true) { 105c5c2eed7Sopenharmony_ci state = false; 106c5c2eed7Sopenharmony_ci Log.info('set window state to false'); 107c5c2eed7Sopenharmony_ci } 108c5c2eed7Sopenharmony_ci this.subWindowForHost = null; 109c5c2eed7Sopenharmony_ci } 110c5c2eed7Sopenharmony_ci 111c5c2eed7Sopenharmony_ci async loadContentAndSetColor(storage: LocalStorage): Promise<boolean> { 112c5c2eed7Sopenharmony_ci let promise: Promise<void> | undefined = 113c5c2eed7Sopenharmony_ci this.subWindowForHost?.loadContent('PermissionSheet/PermissionStateSheetDialog', storage); 114c5c2eed7Sopenharmony_ci if (promise) { 115c5c2eed7Sopenharmony_ci await promise.then(() => { 116c5c2eed7Sopenharmony_ci Log.info('Succeeded in loading the content'); 117c5c2eed7Sopenharmony_ci try { 118c5c2eed7Sopenharmony_ci this.subWindowForHost?.setWindowBackgroundColor('#00000000'); 119c5c2eed7Sopenharmony_ci } catch (error) { 120c5c2eed7Sopenharmony_ci Log.error(`setWindowBackgroundColor failed, error code ${JSON.stringify(error)}`); 121c5c2eed7Sopenharmony_ci } 122c5c2eed7Sopenharmony_ci }).catch((err: BusinessError) => { 123c5c2eed7Sopenharmony_ci Log.error('Failed to load the content. Cause:' + JSON.stringify(err)); 124c5c2eed7Sopenharmony_ci return false; 125c5c2eed7Sopenharmony_ci }) 126c5c2eed7Sopenharmony_ci } 127c5c2eed7Sopenharmony_ci return true; 128c5c2eed7Sopenharmony_ci } 129c5c2eed7Sopenharmony_ci 130c5c2eed7Sopenharmony_ci async createSubWindow(session: UIExtensionContentSession, storage: LocalStorage): Promise<void> { 131c5c2eed7Sopenharmony_ci let extensionWinProxy: uiExtensionHost.UIExtensionHostWindowProxy = session?.getUIExtensionHostWindowProxy(); 132c5c2eed7Sopenharmony_ci setAvoidArea(extensionWinProxy); 133c5c2eed7Sopenharmony_ci let subWindowOpts: window.SubWindowOptions = { 134c5c2eed7Sopenharmony_ci title: '', decorEnabled: false, isModal: true, 135c5c2eed7Sopenharmony_ci }; 136c5c2eed7Sopenharmony_ci if (!extensionWinProxy) { 137c5c2eed7Sopenharmony_ci Log.error('extensionWinProxy is nullptr!'); 138c5c2eed7Sopenharmony_ci return; 139c5c2eed7Sopenharmony_ci } 140c5c2eed7Sopenharmony_ci // try to create subwindow first 141c5c2eed7Sopenharmony_ci await extensionWinProxy.createSubWindowWithOptions('permissionSubWindowForHost' + Date(), subWindowOpts) 142c5c2eed7Sopenharmony_ci .then((subWindow: window.Window)=>{ 143c5c2eed7Sopenharmony_ci this.subWindowForHost = subWindow; 144c5c2eed7Sopenharmony_ci }).catch((err: BusinessError) => { 145c5c2eed7Sopenharmony_ci Log.error('create sub window error:' + err.message + ' ' + err.code); 146c5c2eed7Sopenharmony_ci }); 147c5c2eed7Sopenharmony_ci // if create subwindow fails, try create floating window 148c5c2eed7Sopenharmony_ci if (!this.subWindowForHost) { 149c5c2eed7Sopenharmony_ci Log.error('subwindow is null, failed to create subwindow, tray create float window'); 150c5c2eed7Sopenharmony_ci await this.createFloatingWindow(session); 151c5c2eed7Sopenharmony_ci if (!this.subWindowForHost) { 152c5c2eed7Sopenharmony_ci terminating = true; 153c5c2eed7Sopenharmony_ci PermissionDialogException(Constants.ERR_MODAL_ALREADY_EXIST, session); 154c5c2eed7Sopenharmony_ci this.context.terminateSelf(); 155c5c2eed7Sopenharmony_ci Log.error('floating is null, failed to create float window, terminate self'); 156c5c2eed7Sopenharmony_ci return; 157c5c2eed7Sopenharmony_ci } 158c5c2eed7Sopenharmony_ci } 159c5c2eed7Sopenharmony_ci let dis = display.getDefaultDisplaySync(); 160c5c2eed7Sopenharmony_ci await this.subWindowForHost?.resize(dis.width, dis.height).catch((err: BusinessError) => { 161c5c2eed7Sopenharmony_ci Log.error('resize window error: ' + err.code + ' ' + err.message); 162c5c2eed7Sopenharmony_ci }); 163c5c2eed7Sopenharmony_ci this.subWindowForHost?.on('windowEvent', (eventType: window.WindowEventType) => { 164c5c2eed7Sopenharmony_ci Log.info('window event type is: ' + eventType); 165c5c2eed7Sopenharmony_ci storage.setOrCreate('permissionWindow', eventType); 166c5c2eed7Sopenharmony_ci }) 167c5c2eed7Sopenharmony_ci Log.info('create and resize window success'); 168c5c2eed7Sopenharmony_ci return; 169c5c2eed7Sopenharmony_ci } 170c5c2eed7Sopenharmony_ci 171c5c2eed7Sopenharmony_ci async createFloatingWindow(session: UIExtensionContentSession): Promise<void> { 172c5c2eed7Sopenharmony_ci let config: window.Configuration = { 173c5c2eed7Sopenharmony_ci name: 'PermissionFloatWindow' + Date(), 174c5c2eed7Sopenharmony_ci windowType: window.WindowType.TYPE_FLOAT, 175c5c2eed7Sopenharmony_ci ctx: this.context 176c5c2eed7Sopenharmony_ci } 177c5c2eed7Sopenharmony_ci let promise: Promise<window.Window> | undefined = window.createWindow(config); 178c5c2eed7Sopenharmony_ci await promise.then((floatWindow: window.Window) => { 179c5c2eed7Sopenharmony_ci this.subWindowForHost = floatWindow; 180c5c2eed7Sopenharmony_ci }).catch((err: BusinessError) => { 181c5c2eed7Sopenharmony_ci Log.error('create floating window error:' + err.message + ' ' + err.code); 182c5c2eed7Sopenharmony_ci // if modal already exist , return 183c5c2eed7Sopenharmony_ci if (err.code === Constants.CREATE_WINDOW_REPEATED) { 184c5c2eed7Sopenharmony_ci Log.error('try to create subwindow repeatedly'); 185c5c2eed7Sopenharmony_ci terminating = true; 186c5c2eed7Sopenharmony_ci PermissionDialogException(Constants.ERR_MODAL_ALREADY_EXIST, session); 187c5c2eed7Sopenharmony_ci this.context.terminateSelf(); 188c5c2eed7Sopenharmony_ci } 189c5c2eed7Sopenharmony_ci }); 190c5c2eed7Sopenharmony_ci if (!this.subWindowForHost) { 191c5c2eed7Sopenharmony_ci Log.error('create floating fail'); 192c5c2eed7Sopenharmony_ci return; 193c5c2eed7Sopenharmony_ci } 194c5c2eed7Sopenharmony_ci Log.info('create floating window success'); 195c5c2eed7Sopenharmony_ci } 196c5c2eed7Sopenharmony_ci 197c5c2eed7Sopenharmony_ci showWindow(): void { 198c5c2eed7Sopenharmony_ci this.subWindowForHost?.showWindow((err) => { 199c5c2eed7Sopenharmony_ci if (err && err.code !== 0) { 200c5c2eed7Sopenharmony_ci Log.error(`failed to showWindow for subWindow, ${JSON.stringify(err)}`); 201c5c2eed7Sopenharmony_ci return; 202c5c2eed7Sopenharmony_ci } 203c5c2eed7Sopenharmony_ci this.subWindowForHost?.hideNonSystemFloatingWindows(true); 204c5c2eed7Sopenharmony_ci Log.info('show window and set non floating success'); 205c5c2eed7Sopenharmony_ci }) 206c5c2eed7Sopenharmony_ci } 207c5c2eed7Sopenharmony_ci}