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 }); 19} 20const TAG = 'AVInputCastPicker_component'; 21 22/** 23 * Definition of av cast picker state. 24 */ 25export let AVCastPickerState; 26(function (AVCastPickerState) { 27 /** 28 * The picker starts showing. 29 */ 30 AVCastPickerState[AVCastPickerState['STATE_APPEARING'] = 0] = 'STATE_APPEARING'; 31 /** 32 * The picker finishes presenting. 33 */ 34 AVCastPickerState[AVCastPickerState['STATE_DISAPPEARING'] = 1] = 'STATE_DISAPPEARING'; 35})(AVCastPickerState || (AVCastPickerState = {})); 36 37export class AVInputCastPicker extends ViewPU { 38 constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { 39 super(parent, __localStorage, elmtId, extraInfo); 40 if (typeof paramsLambda === 'function') { 41 this.paramsGenerator_ = paramsLambda; 42 } 43 this.customPicker = undefined; 44 this.__deviceInfoList = new ObservedPropertyObjectPU([], this, 'deviceInfoList'); 45 this.__touchMenuItemIndex = new ObservedPropertySimplePU(-1, this, 'touchMenuItemIndex'); 46 this.__isDarkMode = new ObservedPropertySimplePU(false, this, 'isDarkMode'); 47 this.__isRTL = new ObservedPropertySimplePU(false, this, 'isRTL'); 48 this.onStateChange = undefined; 49 this.extensionProxy = null; 50 this.pickerClickTime = -1; 51 this.pickerCountOnCreation = 0; 52 this.setInitiallyProvidedValue(params); 53 this.finalizeConstruction(); 54 } 55 56 setInitiallyProvidedValue(params) { 57 if (params.customPicker !== undefined) { 58 this.customPicker = params.customPicker; 59 } 60 if (params.deviceInfoList !== undefined) { 61 this.deviceInfoList = params.deviceInfoList; 62 } 63 if (params.touchMenuItemIndex !== undefined) { 64 this.touchMenuItemIndex = params.touchMenuItemIndex; 65 } 66 if (params.isDarkMode !== undefined) { 67 this.isDarkMode = params.isDarkMode; 68 } 69 if (params.isRTL !== undefined) { 70 this.isRTL = params.isRTL; 71 } 72 if (params.onStateChange !== undefined) { 73 this.onStateChange = params.onStateChange; 74 } 75 if (params.extensionProxy !== undefined) { 76 this.extensionProxy = params.extensionProxy; 77 } 78 if (params.pickerClickTime !== undefined) { 79 this.pickerClickTime = params.pickerClickTime; 80 } 81 if (params.pickerCountOnCreation !== undefined) { 82 this.pickerCountOnCreation = params.pickerCountOnCreation; 83 } 84 } 85 86 updateStateVars(params) { 87 } 88 89 purgeVariableDependenciesOnElmtId(rmElmtId) { 90 this.__deviceInfoList.purgeDependencyOnElmtId(rmElmtId); 91 this.__touchMenuItemIndex.purgeDependencyOnElmtId(rmElmtId); 92 this.__isDarkMode.purgeDependencyOnElmtId(rmElmtId); 93 this.__isRTL.purgeDependencyOnElmtId(rmElmtId); 94 } 95 96 aboutToBeDeleted() { 97 this.__deviceInfoList.aboutToBeDeleted(); 98 this.__touchMenuItemIndex.aboutToBeDeleted(); 99 this.__isDarkMode.aboutToBeDeleted(); 100 this.__isRTL.aboutToBeDeleted(); 101 SubscriberManager.Get().delete(this.id__()); 102 this.aboutToBeDeletedInternal(); 103 } 104 105 get deviceInfoList() { 106 return this.__deviceInfoList.get(); 107 } 108 109 set deviceInfoList(newValue) { 110 this.__deviceInfoList.set(newValue); 111 } 112 113 get touchMenuItemIndex() { 114 return this.__touchMenuItemIndex.get(); 115 } 116 117 set touchMenuItemIndex(newValue) { 118 this.__touchMenuItemIndex.set(newValue); 119 } 120 121 get isDarkMode() { 122 return this.__isDarkMode.get(); 123 } 124 125 set isDarkMode(newValue) { 126 this.__isDarkMode.set(newValue); 127 } 128 129 get isRTL() { 130 return this.__isRTL.get(); 131 } 132 133 set isRTL(newValue) { 134 this.__isRTL.set(newValue); 135 } 136 137 aboutToAppear() { 138 AVInputCastPicker.currentPickerCount += 1; 139 this.pickerCountOnCreation = AVInputCastPicker.currentPickerCount; 140 } 141 142 aboutToDisappear() { 143 AVInputCastPicker.currentPickerCount -= 1; 144 } 145 146 iconBuilder(item, isSelected, parent = null) { 147 this.observeComponentCreation2((elmtId, isInitialRender) => { 148 SymbolGlyph.create(!isSelected ? { 149 'id': -1, 150 'type': -1, 151 params: [item.deviceIconName], 152 'bundleName': '__harDefaultBundleName__', 153 'moduleName': '__harDefaultModuleName__' 154 } : { 155 'id': -1, 156 'type': -1, 157 params: [item.selectedIconName], 158 'bundleName': '__harDefaultBundleName__', 159 'moduleName': '__harDefaultModuleName__' 160 }); 161 SymbolGlyph.fontSize('24vp'); 162 SymbolGlyph.fontColor((isSelected && !this.isDarkMode) ? 163 [{ 164 'id': -1, 165 'type': 10001, 166 params: ['sys.color.comp_background_emphasize'], 167 'bundleName': '__harDefaultBundleName__', 168 'moduleName': '__harDefaultModuleName__' 169 }] : [{ 170 'id': -1, 171 'type': 10001, 172 params: ['sys.color.icon_primary'], 173 'bundleName': '__harDefaultBundleName__', 174 'moduleName': '__harDefaultModuleName__' 175 }]); 176 SymbolGlyph.renderingStrategy(SymbolRenderingStrategy.SINGLE); 177 }, SymbolGlyph); 178 } 179 180 textBuilder(item, parent = null) { 181 this.observeComponentCreation2((elmtId, isInitialRender) => { 182 Text.create(item.deviceName); 183 Text.fontSize({ 184 'id': -1, 185 'type': 10002, 186 params: ['sys.float.ohos_id_text_size_body2'], 187 'bundleName': '__harDefaultBundleName__', 188 'moduleName': '__harDefaultModuleName__' 189 }); 190 Text.fontColor(item.isConnected ? (!this.isDarkMode ? { 191 'id': -1, 192 'type': 10001, 193 params: ['sys.color.font_emphasize'], 194 'bundleName': '__harDefaultBundleName__', 195 'moduleName': '__harDefaultModuleName__' 196 } : { 197 'id': -1, 198 'type': 10001, 199 params: ['sys.color.font_primary'], 200 'bundleName': '__harDefaultBundleName__', 201 'moduleName': '__harDefaultModuleName__' 202 }) : 203 (!this.isDarkMode ? { 204 'id': -1, 205 'type': 10001, 206 params: ['sys.color.font_primary'], 207 'bundleName': '__harDefaultBundleName__', 208 'moduleName': '__harDefaultModuleName__' 209 } : { 210 'id': -1, 211 'type': 10001, 212 params: ['sys.color.font_secondary'], 213 'bundleName': '__harDefaultBundleName__', 214 'moduleName': '__harDefaultModuleName__' 215 })); 216 Text.width(254); 217 Text.padding({ 218 left: 8, 219 top: 11, 220 right: 8, 221 bottom: 11 222 }); 223 Text.textOverflow({ overflow: TextOverflow.Ellipsis }); 224 Text.maxLines(2); 225 Text.wordBreak(WordBreak.BREAK_WORD); 226 Text.direction(this.isRTL ? Direction.Rtl : Direction.Ltr); 227 }, Text); 228 Text.pop(); 229 } 230 231 deviceMenu(parent = null) { 232 this.observeComponentCreation2((elmtId, isInitialRender) => { 233 Column.create(); 234 Column.width(326); 235 Column.borderRadius(8); 236 }, Column); 237 this.observeComponentCreation2((elmtId, isInitialRender) => { 238 ForEach.create(); 239 const forEachItemGenFunction = (_item, index) => { 240 const item = _item; 241 this.observeComponentCreation2((elmtId, isInitialRender) => { 242 Flex.create({ 243 direction: FlexDirection.Column, 244 justifyContent: FlexAlign.SpaceBetween, 245 alignItems: ItemAlign.End 246 }); 247 Flex.width('100%'); 248 Flex.onClick(() => { 249 console.info(TAG, ` item click ${item.isConnected}`); 250 if (this.extensionProxy !== null && !item.isConnected) { 251 this.extensionProxy.send({ 'selectedDeviceInfo': item }); 252 } 253 }); 254 }, Flex); 255 this.observeComponentCreation2((elmtId, isInitialRender) => { 256 Flex.create({ 257 direction: FlexDirection.Row, 258 justifyContent: FlexAlign.SpaceBetween, 259 alignItems: ItemAlign.Center 260 }); 261 Flex.constraintSize({ minHeight: 40 }); 262 Flex.padding({ left: 12, right: 12 }); 263 Flex.onTouch((event) => { 264 if (event.type === TouchType.Down) { 265 this.touchMenuItemIndex = index; 266 } else if (event.type === TouchType.Up) { 267 this.touchMenuItemIndex = -1; 268 } 269 }); 270 Flex.backgroundColor(this.touchMenuItemIndex === index ? { 271 'id': -1, 272 'type': 10001, 273 params: ['sys.color.interactive_click'], 274 'bundleName': '__harDefaultBundleName__', 275 'moduleName': '__harDefaultModuleName__' 276 } : '#00FFFFFF'); 277 Flex.borderRadius(this.touchMenuItemIndex === index ? { 278 'id': -1, 279 'type': 10002, 280 params: ['sys.float.corner_radius_level2'], 281 'bundleName': '__harDefaultBundleName__', 282 'moduleName': '__harDefaultModuleName__' 283 } : 0); 284 }, Flex); 285 this.observeComponentCreation2((elmtId, isInitialRender) => { 286 Row.create(); 287 Row.alignItems(VerticalAlign.Center); 288 }, Row); 289 this.iconBuilder.bind(this)(item, false); 290 this.textBuilder.bind(this)(item); 291 Row.pop(); 292 this.observeComponentCreation2((elmtId, isInitialRender) => { 293 If.create(); 294 if (item.isConnected && item.selectedIconName !== null && item.selectedIconName !== undefined) { 295 this.ifElseBranchUpdateFunction(0, () => { 296 this.observeComponentCreation2((elmtId, isInitialRender) => { 297 Row.create(); 298 Row.alignItems(VerticalAlign.Center); 299 }, Row); 300 this.iconBuilder.bind(this)(item, true); 301 Row.pop(); 302 }); 303 } else { 304 this.ifElseBranchUpdateFunction(1, () => { 305 }); 306 } 307 }, If); 308 If.pop(); 309 Flex.pop(); 310 this.observeComponentCreation2((elmtId, isInitialRender) => { 311 If.create(); 312 if (index !== this.deviceInfoList.length - 1) { 313 this.ifElseBranchUpdateFunction(0, () => { 314 this.observeComponentCreation2((elmtId, isInitialRender) => { 315 Row.create(); 316 Row.width('100%'); 317 Row.height(2); 318 }, Row); 319 Row.pop(); 320 }); 321 } else { 322 this.ifElseBranchUpdateFunction(1, () => { 323 }); 324 } 325 }, If); 326 If.pop(); 327 Flex.pop(); 328 }; 329 this.forEachUpdateFunction(elmtId, this.deviceInfoList, forEachItemGenFunction, undefined, true, false); 330 }, ForEach); 331 ForEach.pop(); 332 Column.pop(); 333 } 334 335 buildDefaultPicker(isCustomPicker, parent = null) { 336 this.observeComponentCreation2((elmtId, isInitialRender) => { 337 UIExtensionComponent.create({ 338 abilityName: 'AVInputCastPickerAbility', 339 bundleName: 'com.hmos.mediacontroller', 340 parameters: { 341 'ability.want.params.uiExtensionType': 'sysPicker/mediaControl', 342 'isCustomPicker': isCustomPicker, 343 'currentPickerCount': this.pickerCountOnCreation, 344 } 345 }); 346 UIExtensionComponent.size({ width: '100%', height: '100%' }); 347 UIExtensionComponent.bindMenu({ 348 builder: () => { 349 this.deviceMenu.call(this); 350 } 351 }, { 352 onDisappear: () => { 353 this.touchMenuItemIndex = -1; 354 if (this.onStateChange !== null && this.onStateChange !== undefined) { 355 this.onStateChange(AVCastPickerState.STATE_DISAPPEARING); 356 } 357 }, 358 onAppear: () => { 359 if (this.onStateChange !== null && this.onStateChange !== undefined) { 360 this.onStateChange(AVCastPickerState.STATE_APPEARING); 361 } 362 if (this.extensionProxy !== null && this.pickerClickTime !== -1) { 363 this.extensionProxy.send({ 'timeCost': new Date().getTime() - this.pickerClickTime }); 364 this.pickerClickTime = -1; 365 } 366 } 367 }); 368 UIExtensionComponent.onClick(() => { 369 this.pickerClickTime = new Date().getTime(); 370 }); 371 UIExtensionComponent.onRemoteReady((proxy) => { 372 console.info(TAG, 'onRemoteReady'); 373 this.extensionProxy = proxy; 374 }); 375 UIExtensionComponent.onReceive((data) => { 376 if (JSON.stringify(data['deviceInfoList']) !== undefined) { 377 this.deviceInfoList = JSON.parse(JSON.stringify(data['deviceInfoList'])); 378 } 379 if (JSON.stringify(data['isDarkMode']) !== undefined) { 380 console.info(TAG, `isDarkMode : ${JSON.stringify(data['isDarkMode'])}`); 381 this.isDarkMode = data['isDarkMode']; 382 } 383 if (JSON.stringify(data['isRTL']) !== undefined) { 384 console.info(TAG, `isRTL : ${JSON.stringify(data['isRTL'])}`); 385 this.isRTL = data['isRTL']; 386 } 387 }); 388 UIExtensionComponent.onTerminated((info) => { 389 console.info(TAG, ` onTerminated code: ${info?.code}`); 390 }); 391 UIExtensionComponent.onError((error) => { 392 console.info(TAG, ` onError code: ${error?.code} message: ${error?.message}`); 393 }); 394 }, UIExtensionComponent); 395 } 396 397 buildCustomPicker(parent = null) { 398 this.observeComponentCreation2((elmtId, isInitialRender) => { 399 Stack.create({ alignContent: Alignment.Center }); 400 Stack.size({ width: '100%', height: '100%' }); 401 }, Stack); 402 this.observeComponentCreation2((elmtId, isInitialRender) => { 403 Column.create(); 404 Column.alignItems(HorizontalAlign.Center); 405 Column.justifyContent(FlexAlign.Center); 406 Column.size({ width: '100%', height: '100%' }); 407 Column.zIndex(0); 408 }, Column); 409 this.customPicker.bind(this)(); 410 Column.pop(); 411 this.observeComponentCreation2((elmtId, isInitialRender) => { 412 Column.create(); 413 Column.alignItems(HorizontalAlign.Center); 414 Column.justifyContent(FlexAlign.Center); 415 Column.size({ width: '100%', height: '100%' }); 416 Column.zIndex(1); 417 }, Column); 418 this.buildDefaultPicker.bind(this)(true); 419 Column.pop(); 420 Stack.pop(); 421 } 422 423 initialRender() { 424 this.observeComponentCreation2((elmtId, isInitialRender) => { 425 Column.create(); 426 Column.size({ width: '100%', height: '100%' }); 427 }, Column); 428 this.observeComponentCreation2((elmtId, isInitialRender) => { 429 If.create(); 430 if (this.customPicker === undefined) { 431 this.ifElseBranchUpdateFunction(0, () => { 432 this.buildDefaultPicker.bind(this)(false); 433 }); 434 } else { 435 this.ifElseBranchUpdateFunction(1, () => { 436 this.buildCustomPicker.bind(this)(); 437 }); 438 } 439 }, If); 440 If.pop(); 441 Column.pop(); 442 } 443 444 rerender() { 445 this.updateDirtyElements(); 446 } 447} 448 449AVInputCastPicker.currentPickerCount = 0; 450export default { AVInputCastPicker };