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