1/* 2 * Copyright (c) 2025 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 */ 15 16if (!('finalizeConstruction' in ViewPU.prototype)) { 17 Reflect.set(ViewPU.prototype, 'finalizeConstruction', () => { }); 18} 19const hilog = requireNapi('hilog'); 20const abilityManager = requireNapi('app.ability.abilityManager'); 21const commonEventManager = requireNapi('commonEventManager'); 22const EMBEDDED_HALF_MODE = 2; 23const atomicServiceDataTag = 'ohos.atomicService.window'; 24const ERR_CODE_ABNORMAL = 100014; 25const ERR_CODE_CAPABILITY_NOT_SUPPORT = 801; 26const LOG_TAG = 'HalfScreenLaunchComponent'; 27export class HalfScreenLaunchComponent extends ViewPU { 28 constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { 29 super(parent, __localStorage, elmtId, extraInfo); 30 if (typeof paramsLambda === 'function') { 31 this.paramsGenerator_ = paramsLambda; 32 } 33 this.content = this.doNothingBuilder; 34 this.context = getContext(this); 35 this.appId = ''; 36 this.options = undefined; 37 this.__isShow = new ObservedPropertySimplePU(false, this, 'isShow'); 38 this.subscriber = null; 39 this.onError = undefined; 40 this.onTerminated = undefined; 41 this.onReceive = undefined; 42 this.setInitiallyProvidedValue(params); 43 this.finalizeConstruction(); 44 } 45 setInitiallyProvidedValue(params) { 46 if (params.content !== undefined) { 47 this.content = params.content; 48 } 49 if (params.context !== undefined) { 50 this.context = params.context; 51 } 52 if (params.appId !== undefined) { 53 this.appId = params.appId; 54 } 55 if (params.options !== undefined) { 56 this.options = params.options; 57 } 58 if (params.isShow !== undefined) { 59 this.isShow = params.isShow; 60 } 61 if (params.subscriber !== undefined) { 62 this.subscriber = params.subscriber; 63 } 64 if (params.onError !== undefined) { 65 this.onError = params.onError; 66 } 67 if (params.onTerminated !== undefined) { 68 this.onTerminated = params.onTerminated; 69 } 70 if (params.onReceive !== undefined) { 71 this.onReceive = params.onReceive; 72 } 73 } 74 updateStateVars(params) { 75 } 76 purgeVariableDependenciesOnElmtId(rmElmtId) { 77 this.__isShow.purgeDependencyOnElmtId(rmElmtId); 78 } 79 aboutToBeDeleted() { 80 this.__isShow.aboutToBeDeleted(); 81 SubscriberManager.Get().delete(this.id__()); 82 this.aboutToBeDeletedInternal(); 83 } 84 get isShow() { 85 return this.__isShow.get(); 86 } 87 set isShow(newValue) { 88 this.__isShow.set(newValue); 89 } 90 aboutToAppear() { 91 let subscribeInfo = { 92 events: [commonEventManager.Support.COMMON_EVENT_DISTRIBUTED_ACCOUNT_LOGOUT], 93 }; 94 commonEventManager.createSubscriber(subscribeInfo, (err, data) => { 95 if (err) { 96 hilog.error(0x3900, LOG_TAG, 'Failed to create subscriber, err: %{public}s.', JSON.stringify(err)); 97 return; 98 } 99 if (data === null || data === undefined) { 100 hilog.error(0x3900, LOG_TAG, 'Failed to create subscriber, data is null.'); 101 return; 102 } 103 this.subscriber = data; 104 commonEventManager.subscribe(this.subscriber, (err, data) => { 105 if (err) { 106 hilog.error(0x3900, LOG_TAG, 'Failed to subscribe common event, err: %{public}s.', JSON.stringify(err)); 107 return; 108 } 109 this.isShow = false; 110 }); 111 }); 112 } 113 aboutToDisappear() { 114 if (this.subscriber !== null) { 115 commonEventManager.unsubscribe(this.subscriber, (err) => { 116 if (err) { 117 hilog.error(0x3900, LOG_TAG, 'UnsubscribeCallBack, err: %{public}s.', JSON.stringify(err)); 118 } 119 else { 120 this.subscriber = null; 121 } 122 }); 123 } 124 } 125 doNothingBuilder(parent = null) { 126 } 127 resetOptions() { 128 if (this.options?.parameters) { 129 this.options.parameters['ohos.extra.param.key.showMode'] = EMBEDDED_HALF_MODE; 130 this.options.parameters['ability.want.params.IsNotifyOccupiedAreaChange'] = true; 131 this.options.parameters['ability.want.params.IsModal'] = true; 132 hilog.info(0x3900, LOG_TAG, 'replaced options is %{public}s !', JSON.stringify(this.options)); 133 } 134 else { 135 this.options = { 136 parameters: { 137 'ohos.extra.param.key.showMode': EMBEDDED_HALF_MODE, 138 'ability.want.params.IsNotifyOccupiedAreaChange': true, 139 'ability.want.params.IsModal': true 140 } 141 }; 142 } 143 } 144 async checkAbility() { 145 if (this.isShow) { 146 hilog.error(0x3900, LOG_TAG, 'EmbeddedAbility already shows'); 147 this.isShow = false; 148 return; 149 } 150 this.resetOptions(); 151 try { 152 abilityManager.queryAtomicServiceStartupRule(this.context, this.appId) 153 .then((data) => { 154 if (data.isOpenAllowed) { 155 if (data.isEmbeddedAllowed) { 156 this.isShow = true; 157 hilog.info(0x3900, LOG_TAG, 'EmbeddedOpen is Allowed!'); 158 } else { 159 this.popUp(); 160 } 161 } else { 162 hilog.info(0x3900, LOG_TAG, 'is not allowed open!'); 163 } 164 }).catch((err) => { 165 hilog.error(0x3900, LOG_TAG, 'queryAtomicServiceStartupRule called error!%{public}s', err.message); 166 if (ERR_CODE_CAPABILITY_NOT_SUPPORT === err.code) { 167 this.popUp(); 168 } 169 }); 170 } catch (err) { 171 hilog.error(0x3900, LOG_TAG, 'AtomicServiceStartupRule failed: %{public}s', err.message); 172 this.popUp(); 173 } 174 } 175 async popUp() { 176 this.isShow = false; 177 try { 178 const ability = await this.context.openAtomicService(this.appId, this.options); 179 hilog.info(0x3900, LOG_TAG, '%{public}s open service success!', ability.want); 180 } 181 catch (e) { 182 hilog.error(0x3900, LOG_TAG, '%{public}s open service error!', e.message); 183 } 184 } 185 handleOnReceiveEvent(data) { 186 if (data === undefined || data === null) { 187 return; 188 } 189 if (this.onReceive !== undefined) { 190 const sourceKeys = Object.keys(data); 191 let atomicServiceData = {}; 192 for (let i = 0; i < sourceKeys.length; i++) { 193 if (sourceKeys[i].includes(atomicServiceDataTag)) { 194 atomicServiceData[sourceKeys[i]] = data[sourceKeys[i]]; 195 } 196 } 197 this.onReceive(atomicServiceData); 198 } 199 } 200 initialRender() { 201 this.observeComponentCreation2((elmtId, isInitialRender) => { 202 Row.create(); 203 Row.justifyContent(FlexAlign.Center); 204 Row.onClick(() => { 205 hilog.info(0x3900, LOG_TAG, 'on start atomicservice'); 206 this.checkAbility(); 207 }); 208 Row.bindContentCover({ value: this.isShow, changeEvent: newValue => { this.isShow = newValue; } }, { 209 builder: () => { 210 this.uiExtensionBuilder.call(this); 211 } 212 }, { 213 modalTransition: ModalTransition.NONE, 214 enableSafeArea: true 215 }); 216 }, Row); 217 this.content.bind(this)(); 218 Row.pop(); 219 } 220 uiExtensionBuilder(parent = null) { 221 this.observeComponentCreation2((elmtId, isInitialRender) => { 222 Column.create(); 223 Column.height(LayoutPolicy.matchParent); 224 Column.width(LayoutPolicy.matchParent); 225 Column.ignoreLayoutSafeArea([LayoutSafeAreaType.SYSTEM], [LayoutSafeAreaEdge.TOP, LayoutSafeAreaEdge.BOTTOM]); 226 }, Column); 227 this.observeComponentCreation2((elmtId, isInitialRender) => { 228 UIExtensionComponent.create({ 229 bundleName: `com.atomicservice.${this.appId}`, 230 flags: this.options?.flags, 231 parameters: this.options?.parameters 232 }, 233 { 234 windowModeFollowStrategy: WindowModeFollowStrategy.FOLLOW_HOST_WINDOW_MODE 235 }); 236 UIExtensionComponent.height('100%'); 237 UIExtensionComponent.width('100%'); 238 UIExtensionComponent.backgroundColor(Color.Transparent); 239 UIExtensionComponent.onError(err => { 240 if (this.onError) { 241 this.onError(err); 242 } 243 this.isShow = false; 244 hilog.error(0x3900, LOG_TAG, 'call up UIExtension error!%{public}s', err.message); 245 if (err.code !== ERR_CODE_ABNORMAL) { 246 this.getUIContext().showAlertDialog({ 247 message: err.message 248 }); 249 } 250 }); 251 UIExtensionComponent.onTerminated(info => { 252 this.isShow = false; 253 if (this.onTerminated) { 254 this.onTerminated(info); 255 } 256 }); 257 UIExtensionComponent.onReceive(data => { 258 this.handleOnReceiveEvent(data); 259 }); 260 }, UIExtensionComponent); 261 Column.pop(); 262 } 263 rerender() { 264 this.updateDirtyElements(); 265 } 266} 267 268export default { HalfScreenLaunchComponent };