• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 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 curves from '@ohos.curves';
17import PiPWindow from '@ohos.PiPWindow';
18import pip from '@ohos.pip';
19import window from '@ohos.window';
20
21const TAG: string = 'PiPLive';
22const TIMEOUT: number = 3000;
23
24@Styles
25function fillSize() {
26  .size({ width: '100%', height: '100%' })
27}
28
29@Entry
30@Component
31export struct PiPLive {
32  xComponentId: string = 'pip';
33  windowType: PiPWindow.PiPTemplateType = PiPWindow.PiPTemplateType.VIDEO_LIVE;
34  private hideEventId: number = -1;
35  @State private showControl: boolean = false;
36  private xComponentController: XComponentController = new XComponentController();
37  private surfaceId: string = '';
38  private controlTransEffect: TransitionEffect = TransitionEffect.OPACITY;
39  @Provide @Watch('onHideControlNow') hideControlNow: boolean = false;
40  @Provide @Watch('onHideControlDelay') hideControlDelay: boolean = false;
41
42  onHideControlNow() {
43    if (this.hideControlNow) {
44      this.switchToHideWithoutAnime();
45    }
46    this.hideControlNow = false;
47  }
48
49  onHideControlDelay() {
50    if (this.hideControlDelay) {
51      this.delayHide();
52    }
53    this.hideControlDelay = false;
54  }
55
56  switchToShow() {
57    animateTo({ curve: curves.responsiveSpringMotion(0.25, 1) }, () => {
58      this.showControl = true;
59    });
60    this.delayHide();
61  }
62
63  switchToHide() {
64    if (this.hideEventId !== -1) {
65      clearTimeout(this.hideEventId);
66    }
67    animateTo({ curve: curves.responsiveSpringMotion(0.25, 1) }, () => {
68      this.showControl = false;
69    });
70  }
71
72  switchToHideWithoutAnime() {
73    if (this.hideEventId !== -1) {
74      clearTimeout(this.hideEventId);
75    }
76    this.showControl = false;
77  }
78
79  delayHide() {
80    if (this.hideEventId !== -1) {
81      clearTimeout(this.hideEventId);
82    }
83    this.hideEventId = this.showControl ?
84    setTimeout(() => {
85      animateTo({ curve: curves.responsiveSpringMotion(0.25, 1) }, () => {
86        this.showControl = false;
87      });
88    }, TIMEOUT) :
89      -1;
90  }
91
92  build() {
93    Stack() {
94      XComponent({ id: this.xComponentId, type: 'surface', controller: this.xComponentController })
95        .onLoad(() => {
96          pip.initXComponentController(this.xComponentController);
97          console.debug(TAG, 'XComponent onLoad done');
98        })
99        .fillSize();
100
101      RelativeContainer() {
102        Stack()
103          .fillSize()
104          .id('fill_stack')
105        if (this.showControl) {
106          RelativeContainer() {
107            DefaultControl();
108            LiveControl();
109          }
110          .fillSize()
111          .transition(this.controlTransEffect)
112          .alignRules({
113            top: { anchor: '__container__', align: VerticalAlign.Top },
114            right: { anchor: '__container__', align: HorizontalAlign.End }
115          })
116          .id('control_inner')
117        }
118      }
119      .fillSize()
120      .id('control')
121      .gesture(
122        GestureGroup(GestureMode.Exclusive,
123          TapGesture({ count: 2 })
124            .onAction((event: GestureEvent) => {
125              this.switchToHideWithoutAnime();
126              pip.processScale();
127            }),
128          TapGesture({ count: 1 })
129            .onAction((event: GestureEvent) => {
130              if (this.showControl) {
131                this.switchToHide();
132              } else {
133                this.switchToShow();
134              }
135            }),
136          PanGesture()
137            .onActionStart((event: GestureEvent) => {
138              this.switchToHide();
139              pip.startMove();
140            })
141        )
142      )
143    }
144    .fillSize()
145  }
146}
147
148@Component
149struct DefaultControl {
150  @Consume hideControlNow: boolean;
151
152  build() {
153    RelativeContainer() {
154      Button({ type: ButtonType.Circle }) {
155        Image($r('sys.media.ohos_ic_public_close'))
156          .size({ width: 24, height: 24 })
157          .fillColor($r('sys.color.ohos_id_color_primary_contrary'))
158          .objectFit(ImageFit.Contain)
159      }
160      .backgroundColor('#00FFFFFF')
161      .size({ width: 24, height: 24 })
162      .margin(12)
163      .alignRules({
164        center: { anchor: '__container__', align: VerticalAlign.Center },
165        left: { anchor: '__container__', align: HorizontalAlign.Start }
166      })
167      .id('control_exit')
168      .responseRegion({ x: '-50%', y: '-50%', width: '200%', height: '200%' })
169      .onClick(() => {
170        this.hideControlNow = true;
171        pip.close();
172        console.debug(TAG, 'action: exit');
173      })
174
175      Button({ type: ButtonType.Circle }) {
176        Image($r('sys.media.ohos_ic_public_restore'))
177          .fillColor($r('sys.color.ohos_id_color_primary_contrary'))
178          .objectFit(ImageFit.Contain)
179      }
180      .backgroundColor('#00FFFFFF')
181      .size({ width: 24, height: 24 })
182      .margin(12)
183      .alignRules({
184        center: { anchor: '__container__', align: VerticalAlign.Center },
185        right: { anchor: '__container__', align: HorizontalAlign.End }
186      })
187      .id('control_restore')
188      .responseRegion({ x: '-50%', y: '-50%', width: '200%', height: '200%' })
189      .onClick(() => {
190        this.hideControlNow = true;
191        pip.restore();
192        console.debug(TAG, 'action: restore');
193      })
194    }
195    .width('100%')
196    .height(48)
197    .linearGradient({
198      angle: 180,
199      colors: [['#30000000', 0.0], ['#00000000', 1.0]]
200    })
201    .alignRules({
202      top: { anchor: '__container__', align: VerticalAlign.Top },
203      left: { anchor: '__container__', align: HorizontalAlign.Start }
204    })
205    .id('default_control')
206  }
207}
208
209@Component
210struct LiveControl {
211  @State isPlaying: boolean = true;
212  @Consume hideControlDelay: boolean;
213
214  build() {
215    RelativeContainer() {
216      Button({ type: ButtonType.Circle }) {
217        Image(this.isPlaying ? $r('sys.media.ohos_ic_public_pause') : $r('sys.media.ohos_ic_public_play'))
218          .fillColor($r('sys.color.ohos_id_color_primary_contrary'))
219          .objectFit(ImageFit.Contain)
220      }
221      .backgroundColor('#00FFFFFF')
222      .size({ width: 24, height: 24 })
223      .margin(12)
224      .alignRules({
225        top: { anchor: '__container__', align: VerticalAlign.Top },
226        middle: { anchor: '__container__', align: HorizontalAlign.Center }
227      })
228      .id('control_play')
229      .responseRegion({ x: '-50%', y: '-50%', width: '200%', height: '200%' })
230      .onClick(() => {
231        this.isPlaying = !this.isPlaying;
232        this.hideControlDelay = true;
233        pip.triggerAction('playbackStateChanged');
234        console.debug(TAG, 'action: live play');
235      })
236
237      Progress({ value: 0, total: 100, type: ProgressType.Linear })
238        .size({ width: '100%', height: 4 })
239        .color('#33EEEEEE')
240        .padding({ left: 16, right: 16 })
241        .margin({ top: -4, bottom: 8 })
242        .alignRules({
243          bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
244          middle: { anchor: '__container__', align: HorizontalAlign.Center }
245        })
246        .id('control_progress')
247    }
248    .width('100%')
249    .height(56)
250    .linearGradient({
251      angle: 0,
252      colors: [['#30000000', 0.0], ['#00000000', 1.0]]
253    })
254    .alignRules({
255      bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
256      left: { anchor: '__container__', align: HorizontalAlign.Start }
257    })
258    .id('live_control')
259  }
260}
261