• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
16import pip from '@ohos.pip';
17import { NodeController, FrameNode, UIContext, typeNode } from '@kit.ArkUI';
18
19const TAG: string = 'PiPContent';
20const ABOUT_TO_STOP = 3;
21
22class XCNodeController extends NodeController {
23  private mXComponent: typeNode.XComponent;
24  private node: FrameNode | null = null;
25
26  constructor(xComponent: typeNode.XComponent) {
27    super();
28    this.mXComponent = xComponent;
29  }
30
31  makeNode(uiContext: UIContext): FrameNode | null {
32    this.node = new FrameNode(uiContext);
33    this.node.appendChild(this.mXComponent);
34    return this.node;
35  }
36
37  replaceNode(newNode: typeNode.XComponent): void {
38    this.node?.removeChild(this.mXComponent);
39    this.mXComponent = newNode;
40    this.node?.appendChild(this.mXComponent);
41  }
42
43  removeNode() {
44    this.node?.removeChild(this.mXComponent);
45  }
46}
47
48@Entry
49@Component
50struct PiPContent {
51  private xComponentController: XComponentController = new XComponentController();
52  private nodeController: NodeController | null = null;
53  private mXCNodeController: XCNodeController | null = null;
54  @State useNode: boolean = false;
55  @State nodeChange: boolean = false;
56  private xComponent: typeNode.XComponent | null = null;
57  xComponentId: string = 'pipContent';
58  xComponentType: string = 'surface';
59
60  validateNode(node: typeNode.XComponent | null): boolean {
61    if (node === null || node === undefined) {
62      console.error(TAG, `validateNode node is null`);
63      return false;
64    }
65    let type: string = node.getNodeType();
66    if (type !== 'XComponent') {
67      console.error(TAG, `node type mismatch: ${type}`);
68      return false;
69    }
70    return true;
71  }
72
73  private registerUpdateNodeListener() {
74    pip.on('nodeUpdate', this.nodeUpdateListener);
75  }
76
77  private updatePipNodeType(newNode: typeNode.XComponent) {
78    let parent: FrameNode | null = newNode.getParent();
79    if (parent === null || parent === undefined) {
80      pip.setPipNodeType(newNode, false);
81    } else {
82      pip.setPipNodeType(newNode, true);
83      parent.removeChild(newNode);
84    }
85  }
86
87  private registerStateChangeListener() {
88    pip.on('stateChange', this.stateChangeListener)
89  }
90
91  private nodeUpdateListener = (newNode: typeNode.XComponent) => {
92    console.info(TAG, `nodeUpdate`);
93    if (!this.validateNode(newNode)) {
94      return;
95    }
96    if (this.useNode) {
97      pip.setPipNodeType(this.xComponent, false);
98      this.updatePipNodeType(newNode);
99      this.mXCNodeController?.replaceNode(newNode);
100      this.nodeChange = true;
101    } else {
102      this.updatePipNodeType(newNode);
103      this.mXCNodeController = new XCNodeController(newNode);
104      console.info(TAG, 'update to Node Controller');
105      this.registerStateChangeListener();
106      this.useNode = true;
107    }
108  };
109
110  private stateChangeListener = (state: number) => {
111    console.info(TAG, `stateChange state:${state}`);
112    if (state === ABOUT_TO_STOP) {
113      this.mXCNodeController?.removeNode();
114    }
115  };
116
117  aboutToAppear(): void {
118    try {
119      this.nodeController = pip.getCustomUIController();
120      this.registerUpdateNodeListener();
121      this.xComponent = pip.getTypeNode();
122      if (!this.validateNode(this.xComponent)) {
123        return;
124      }
125      if (this.xComponent === null) {
126        console.error(TAG, `validateNode node is null`);
127        return;
128      }
129      this.useNode = true;
130      this.updatePipNodeType(this.xComponent);
131      pip.setTypeNodeEnabled();
132      this.mXCNodeController = new XCNodeController(this.xComponent);
133      console.info(TAG, 'use Node Controller');
134      this.registerStateChangeListener();
135    } catch (err) {
136      console.log(`aboutToAppear failed`);
137    }
138  }
139
140  aboutToDisappear(): void {
141    try {
142      pip.off('stateChange', this.stateChangeListener);
143      pip.off('nodeUpdate', this.nodeUpdateListener);
144    } catch (err) {
145      console.log(`aboutToDisappear failed`);
146    }
147  }
148
149  build() {
150    Stack() {
151      if (this.useNode || this.nodeChange) {
152        this.buildNode();
153      } else {
154        this.buildXComponent();
155      }
156      if (this.nodeController !== null) {
157        this.buildCustomUI();
158      }
159    }
160    .size({ width: '100%', height: '100%' });
161  }
162
163  @Builder
164  buildCustomUI() {
165    NodeContainer(this.nodeController)
166      .size({ width: '100%', height: '100%' });
167  }
168
169  @Builder
170  buildXComponent() {
171    XComponent({ id: this.xComponentId, type: this.xComponentType, controller: this.xComponentController })
172      .onLoad(() => {
173        pip.initXComponentController(this.xComponentController);
174        console.debug(TAG, 'XComponent onLoad done');
175      })
176      .size({ width: '100%', height: '100%' })
177      .backgroundColor(Color.Transparent);
178  }
179
180  @Builder
181  buildNode() {
182    NodeContainer(this.mXCNodeController)
183      .size({ width: '100%', height: '100%' })
184  }
185}