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 */ 15 16interface NodeInfo { 17 nodeId: number, 18 nodePtr: NodePtr 19} 20 21class NodeAdapter extends Disposable { 22 nativePtr_: NodePtr; 23 nativeRef_: NativeStrongRef; 24 nodeRefs_: Array<FrameNode> = new Array(); 25 count_: number = 0; 26 attachedNodeRef_: WeakRef<FrameNode>; 27 28 onAttachToNode?: (target: FrameNode) => void; 29 onDetachFromNode?: () => void; 30 onGetChildId?: (index: number) => number; 31 onCreateChild?: (index: number) => FrameNode; 32 onDisposeChild?: (id: number, node: FrameNode) => void; 33 onUpdateChild?: (id: number, node: FrameNode) => void; 34 35 constructor() { 36 super(); 37 this.nativeRef_ = getUINativeModule().nodeAdapter.createAdapter(); 38 this.nativePtr_ = this.nativeRef_.getNativeHandle(); 39 getUINativeModule().nodeAdapter.setCallbacks(this.nativePtr_, this, 40 this.onAttachToNodePtr, this.onDetachFromNodePtr, 41 this.onGetChildId !== undefined ? this.onGetChildId : undefined, 42 this.onCreateChild !== undefined ? this.onCreateNewNodePtr : undefined, 43 this.onDisposeChild !== undefined ? this.onDisposeNodePtr : undefined, 44 this.onUpdateChild !== undefined ? this.onUpdateNodePtr : undefined 45 ); 46 } 47 48 getNodeType(): string { 49 return getUINativeModule().nodeAdapter.getNodeType(this.nativePtr_); 50 } 51 52 dispose(): void { 53 super.dispose(); 54 if (this.nativePtr_) { 55 getUINativeModule().nodeAdapter.fireArkUIObjectLifecycleCallback(new WeakRef(this), 56 'NodeAdapter', this.getNodeType() || 'NodeAdapter', this.nativePtr_); 57 } 58 let hostNode = this.attachedNodeRef_.deref(); 59 if (hostNode !== undefined) { 60 NodeAdapter.detachNodeAdapter(hostNode); 61 } 62 this.nativeRef_.dispose(); 63 this.nativePtr_ = null; 64 } 65 66 isDisposed(): boolean { 67 return super.isDisposed() && (this.nativePtr_ === undefined || this.nativePtr_ === null); 68 } 69 70 set totalNodeCount(count: number) { 71 if (count < 0) { 72 return; 73 } 74 getUINativeModule().nodeAdapter.setTotalNodeCount(this.nativePtr_, count); 75 this.count_ = count; 76 } 77 78 get totalNodeCount(): number { 79 return this.count_; 80 } 81 82 reloadAllItems(): void { 83 getUINativeModule().nodeAdapter.notifyItemReloaded(this.nativePtr_); 84 } 85 86 reloadItem(start: number, count: number): void { 87 if (start < 0 || count < 0) { 88 return; 89 } 90 getUINativeModule().nodeAdapter.notifyItemChanged(this.nativePtr_, start, count); 91 } 92 93 removeItem(start: number, count: number): void { 94 if (start < 0 || count < 0) { 95 return; 96 } 97 getUINativeModule().nodeAdapter.notifyItemRemoved(this.nativePtr_, start, count); 98 } 99 100 insertItem(start: number, count: number): void { 101 if (start < 0 || count < 0) { 102 return; 103 } 104 getUINativeModule().nodeAdapter.notifyItemInserted(this.nativePtr_, start, count); 105 } 106 107 moveItem(from: number, to: number): void { 108 if (from < 0 || to < 0) { 109 return; 110 } 111 getUINativeModule().nodeAdapter.notifyItemMoved(this.nativePtr_, from, to); 112 } 113 114 getAllAvailableItems(): Array<FrameNode> { 115 let result: Array<FrameNode> = new Array(); 116 let nodes: Array<NodeInfo> = getUINativeModule().nodeAdapter.getAllItems(this.nativePtr_); 117 if (nodes !== undefined) { 118 nodes.forEach(node => { 119 let nodeId = node.nodeId; 120 if (FrameNodeFinalizationRegisterProxy.ElementIdToOwningFrameNode_.has(nodeId)) { 121 let frameNode = FrameNodeFinalizationRegisterProxy.ElementIdToOwningFrameNode_.get(nodeId).deref(); 122 result.push(frameNode); 123 } 124 }); 125 } 126 return result; 127 } 128 129 onAttachToNodePtr(target: NodeInfo): void { 130 let nodeId = target.nodeId; 131 if (FrameNodeFinalizationRegisterProxy.ElementIdToOwningFrameNode_.has(nodeId)) { 132 let frameNode = FrameNodeFinalizationRegisterProxy.ElementIdToOwningFrameNode_.get(nodeId).deref(); 133 if (frameNode === undefined) { 134 return; 135 } 136 frameNode.setAdapterRef(this); 137 this.attachedNodeRef_ = new WeakRef(frameNode); 138 if (this.onAttachToNode !== undefined) { 139 this.onAttachToNode(frameNode); 140 } 141 } 142 } 143 144 onDetachFromNodePtr(): void { 145 if (this === undefined) { 146 return; 147 } 148 if (this.onDetachFromNode !== undefined) { 149 this.onDetachFromNode(); 150 } 151 let attachedNode = this.attachedNodeRef_.deref(); 152 if (attachedNode !== undefined) { 153 attachedNode.setAdapterRef(undefined); 154 } 155 this.nodeRefs_.splice(0, this.nodeRefs_.length); 156 } 157 158 onCreateNewNodePtr(index: number): NodePtr { 159 if (this.onCreateChild !== undefined) { 160 let node = this.onCreateChild(index); 161 if (!this.nodeRefs_.includes(node)) { 162 this.nodeRefs_.push(node); 163 } 164 return node.getNodePtr(); 165 } 166 return null; 167 } 168 169 onDisposeNodePtr(id: number, node: NodeInfo): void { 170 let nodeId = node.nodeId; 171 if (FrameNodeFinalizationRegisterProxy.ElementIdToOwningFrameNode_.has(nodeId)) { 172 let frameNode = FrameNodeFinalizationRegisterProxy.ElementIdToOwningFrameNode_.get(nodeId).deref(); 173 if (this.onDisposeChild !== undefined && frameNode !== undefined) { 174 this.onDisposeChild(id, frameNode); 175 let index = this.nodeRefs_.indexOf(frameNode); 176 if (index > -1) { 177 this.nodeRefs_.splice(index, 1); 178 } 179 } 180 } 181 } 182 183 onUpdateNodePtr(id: number, node: NodeInfo): void { 184 let nodeId = node.nodeId; 185 if (FrameNodeFinalizationRegisterProxy.ElementIdToOwningFrameNode_.has(nodeId)) { 186 let frameNode = FrameNodeFinalizationRegisterProxy.ElementIdToOwningFrameNode_.get(nodeId).deref(); 187 if (this.onUpdateChild !== undefined && frameNode !== undefined) { 188 this.onUpdateChild(id, frameNode); 189 } 190 } 191 } 192 193 static attachNodeAdapter(adapter: NodeAdapter, node: FrameNode): boolean { 194 if (node === null || node === undefined) { 195 return false; 196 } 197 if (!node.isModifiable()) { 198 return false; 199 } 200 const hasAttributeProperty = Object.prototype.hasOwnProperty.call(node, 'attribute_'); 201 if (hasAttributeProperty) { 202 let frameeNode = node as TypedFrameNode<ArkComponent>; 203 if (frameeNode.attribute_.allowChildCount !== undefined) { 204 const allowCount = frameeNode.attribute_.allowChildCount(); 205 if (allowCount <= 1) { 206 return false; 207 } 208 } 209 } 210 return getUINativeModule().nodeAdapter.attachNodeAdapter(adapter.nativePtr_, node.getNodePtr()); 211 } 212 213 static detachNodeAdapter(node: FrameNode) { 214 if (node === null || node === undefined) { 215 return; 216 } 217 getUINativeModule().nodeAdapter.detachNodeAdapter(node.getNodePtr()); 218 } 219}