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 hilog from '@ohos.hilog'; 16import abilityManager from '@ohos.app.ability.abilityManager'; 17import common from '@ohos.app.ability.common'; 18import { Callback, BusinessError } from '@ohos.base'; 19import AtomicServiceOptions from '@ohos.app.ability.AtomicServiceOptions'; 20import commonEventManager from '@ohos.commonEventManager'; 21import Base from '@ohos.base'; 22 23export class LaunchController { 24 public launchAtomicService = (appId: string, options?: AtomicServiceOptions) => {}; 25} 26 27const EMBEDDED_FULL_MODE: number = 1; 28const atomicServiceDataTag: string = 'ohos.atomicService.window'; 29const ERR_CODE_ABNORMAL: number = 100014; 30const ERR_CODE_CAPABILITY_NOT_SUPPORT: number = 801; 31const LOG_TAG: string = 'InnerFullScreenLaunchComponent'; 32 33@Component 34export struct InnerFullScreenLaunchComponent { 35 @BuilderParam content: Callback<void> = this.doNothingBuilder; 36 private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; 37 controller: LaunchController = new LaunchController(); 38 private appId: string = ''; 39 private options?: AtomicServiceOptions; 40 @State private isShow: boolean = false; 41 private subscriber: commonEventManager.CommonEventSubscriber | null = null; 42 private launchAtomicService = (appId: string, options?: AtomicServiceOptions) => { 43 hilog.info(0x3900, LOG_TAG, 44 'launchAtomicService, appId: %{public}s.', appId); 45 this.appId = appId; 46 this.options = options; 47 this.checkAbility(); 48 } 49 onReceive?: Callback<Record<string, Object>>; 50 51 aboutToAppear() { 52 let subscribeInfo: commonEventManager.CommonEventSubscribeInfo = { 53 events: [commonEventManager.Support.COMMON_EVENT_DISTRIBUTED_ACCOUNT_LOGOUT], 54 }; 55 56 commonEventManager.createSubscriber(subscribeInfo, 57 (err:Base.BusinessError, data: commonEventManager.CommonEventSubscriber) => { 58 if (err) { 59 hilog.error(0x3900, LOG_TAG, 60 'Failed to create subscriber, err: %{public}s.', err.message); 61 return; 62 } 63 64 if (data == null || data == undefined) { 65 hilog.error(0x3900, LOG_TAG, 'Failed to create subscriber, data is null.'); 66 return; 67 } 68 69 this.subscriber = data; 70 commonEventManager.subscribe(this.subscriber, 71 (err: Base.BusinessError, data: commonEventManager.CommonEventData) => { 72 if (err) { 73 hilog.error(0x3900, LOG_TAG, 74 'Failed to subscribe common event, err: %{public}s.', err.message); 75 return; 76 } 77 78 hilog.info(0x3900, LOG_TAG, 'Received account logout event.'); 79 this.isShow = false; 80 }) 81 }) 82 this.controller.launchAtomicService = this.launchAtomicService; 83 } 84 85 aboutToDisappear() { 86 if (this.subscriber !== null) { 87 commonEventManager.unsubscribe(this.subscriber, (err) => { 88 if (err) { 89 hilog.error(0x3900, LOG_TAG, 90 'UnsubscribeCallBack, err: %{public}s.', err.message); 91 } else { 92 this.subscriber = null; 93 } 94 }) 95 } 96 } 97 98 @Builder 99 doNothingBuilder() { 100 }; 101 102 resetOptions() { 103 if (this.options?.parameters) { 104 this.options.parameters['ohos.extra.param.key.showMode'] = EMBEDDED_FULL_MODE; 105 this.options.parameters['ability.want.params.IsNotifyOccupiedAreaChange'] = true; 106 hilog.info(0x3900, LOG_TAG, 'replaced options is %{public}s !', JSON.stringify(this.options)); 107 } else { 108 this.options = { 109 parameters: { 110 'ohos.extra.param.key.showMode': EMBEDDED_FULL_MODE, 111 'ability.want.params.IsNotifyOccupiedAreaChange': true, 112 } 113 }; 114 } 115 } 116 117 async checkAbility(): Promise<void> { 118 if (this.isShow) { 119 hilog.error(0x3900, LOG_TAG, 'EmbeddedAbility already shows'); 120 this.isShow = false; 121 return; 122 } 123 this.resetOptions(); 124 try { 125 abilityManager.queryAtomicServiceStartupRule(this.context, this.appId) 126 .then((data: abilityManager.AtomicServiceStartupRule) => { 127 if (data.isOpenAllowed) { 128 if (data.isEmbeddedAllowed) { 129 this.isShow = true; 130 hilog.info(0x3900, LOG_TAG, 'EmbeddedOpen is Allowed!'); 131 } else { 132 this.popUp(); 133 } 134 } else { 135 hilog.info(0x3900, LOG_TAG, 'is not allowed open!'); 136 } 137 }).catch((err: BusinessError) => { 138 hilog.error(0x3900, LOG_TAG, 'queryAtomicServiceStartupRule called error!%{public}s', err.message); 139 if (ERR_CODE_CAPABILITY_NOT_SUPPORT === err.code) { 140 this.popUp(); 141 } 142 }); 143 } catch (err: BusinessError) { 144 hilog.error(0x3900, LOG_TAG, 'AtomicServiceStartupRule failed: %{public}s', err.message); 145 this.popUp(); 146 } 147 } 148 149 async popUp(): Promise<void> { 150 this.isShow = false; 151 try { 152 const ability = await this.context.openAtomicService(this.appId, this.options); 153 hilog.info(0x3900, LOG_TAG, '%{public}s open service success!', ability.want); 154 } catch (e) { 155 hilog.error(0x3900, LOG_TAG, '%{public}s open service error!', e.message); 156 } 157 } 158 159 private handleOnReceiveEvent(data: Object): void { 160 if (data === undefined || data === null) { 161 return; 162 } 163 if (this.onReceive !== undefined) { 164 const sourceKeys = Object.keys(data); 165 let atomicServiceData: Record<string, Object> = {}; 166 for (let i = 0; i < sourceKeys.length; i++) { 167 if (sourceKeys[i].includes(atomicServiceDataTag)) { 168 atomicServiceData[sourceKeys[i]] = data[sourceKeys[i]]; 169 } 170 } 171 this.onReceive(atomicServiceData); 172 } 173 } 174 175 build() { 176 Row() { 177 this.content(); 178 } 179 .justifyContent(FlexAlign.Center) 180 .bindContentCover($$this.isShow, this.uiExtensionBuilder(), { 181 modalTransition: ModalTransition.DEFAULT, 182 enableSafeArea: true 183 }) 184 } 185 186 @Builder 187 uiExtensionBuilder() { 188 Column() { 189 UIExtensionComponent({ 190 bundleName: `com.atomicservice.${this.appId}`, 191 flags: this.options?.flags, 192 parameters: this.options?.parameters 193 },{ 194 windowModeFollowStrategy: WindowModeFollowStrategy.FOLLOW_HOST_WINDOW_MODE 195 }) 196 .backgroundColor($r('sys.color.ohos_id_color_titlebar_bg')) 197 .defaultFocus(true) 198 .height('100%') 199 .width('100%') 200 .onRelease( 201 () => { 202 hilog.error(0x3900, LOG_TAG, 'onRelease'); 203 this.isShow = false; 204 } 205 ) 206 .onError( 207 err => { 208 this.isShow = false; 209 hilog.error(0x3900, LOG_TAG, 'call up UIExtension error! %{public}s', err.message); 210 if (err.code !== ERR_CODE_ABNORMAL) { 211 this.getUIContext().showAlertDialog({ 212 message: err.message 213 }); 214 } 215 } 216 ) 217 .onReceive(data => { 218 this.handleOnReceiveEvent(data); 219 }) 220 } 221 .height(LayoutPolicy.matchParent) 222 .width(LayoutPolicy.matchParent) 223 .ignoreLayoutSafeArea([LayoutSafeAreaType.SYSTEM], [LayoutSafeAreaEdge.TOP, LayoutSafeAreaEdge.BOTTOM]) 224 } 225}