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