• 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', (newNode: typeNode.XComponent) => {
75      console.info(TAG, `nodeUpdate`);
76      if (!this.validateNode(newNode)) {
77        return;
78      }
79      if (this.useNode) {
80        pip.setPipNodeType(this.xComponent, false);
81        this.updatePipNodeType(newNode);
82        this.mXCNodeController?.replaceNode(newNode);
83        this.nodeChange = true;
84      } else {
85        this.updatePipNodeType(newNode);
86        this.mXCNodeController = new XCNodeController(newNode);
87        console.info(TAG, 'update to Node Controller');
88        this.registerStateChangeListener();
89        this.useNode = true;
90      }
91    });
92  }
93
94  private updatePipNodeType(newNode: typeNode.XComponent) {
95    let parent: FrameNode | null = newNode.getParent();
96    if (parent === null || parent === undefined) {
97      pip.setPipNodeType(newNode, false);
98    } else {
99      pip.setPipNodeType(newNode, true);
100      parent.removeChild(newNode);
101    }
102  }
103
104  private registerStateChangeListener() {
105    pip.on('stateChange', (state: number) => {
106      console.info(TAG, `stateChange state:${state}`);
107      if (state === ABOUT_TO_STOP) {
108        this.mXCNodeController?.removeNode();
109      }
110    })
111  }
112
113  aboutToAppear(): void {
114    this.nodeController = pip.getCustomUIController();
115    this.registerUpdateNodeListener();
116    this.xComponent = pip.getTypeNode();
117    if (!this.validateNode(this.xComponent)) {
118      return;
119    }
120    if (this.xComponent === null) {
121      console.error(TAG, `validateNode node is null`);
122      return;
123    }
124    this.useNode = true;
125    this.updatePipNodeType(this.xComponent);
126    pip.setTypeNodeEnabled();
127    this.mXCNodeController = new XCNodeController(this.xComponent);
128    console.info(TAG, 'use Node Controller');
129    this.registerStateChangeListener();
130  }
131
132  aboutToDisappear(): void {
133    pip.off('stateChange');
134    pip.off('nodeUpdate');
135  }
136
137  build() {
138    Stack() {
139      if (this.useNode || this.nodeChange) {
140        this.buildNode();
141      } else {
142        this.buildXComponent();
143      }
144      if (this.nodeController !== null) {
145        this.buildCustomUI();
146      }
147    }
148    .size({ width: '100%', height: '100%' });
149  }
150
151  @Builder
152  buildCustomUI() {
153    NodeContainer(this.nodeController)
154      .size({ width: '100%', height: '100%' });
155  }
156
157  @Builder
158  buildXComponent() {
159    XComponent({ id: this.xComponentId, type: this.xComponentType, controller: this.xComponentController })
160      .onLoad(() => {
161        pip.initXComponentController(this.xComponentController);
162        console.debug(TAG, 'XComponent onLoad done');
163      })
164      .size({ width: '100%', height: '100%' })
165      .backgroundColor(Color.Transparent);
166  }
167
168  @Builder
169  buildNode() {
170    NodeContainer(this.mXCNodeController)
171      .size({ width: '100%', height: '100%' })
172  }
173}