• 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';
19
20const TAG: string = 'PiPMeeting';
21const TIMEOUT: number = 3000;
22
23@Styles
24function fillSize() {
25  .size({ width: '100%', height: '100%' })
26}
27
28@Entry
29@Component
30export struct PiPMeeting {
31  xComponentId: string = 'pip';
32  windowType: PiPWindow.PiPTemplateType = PiPWindow.PiPTemplateType.VIDEO_MEETING;
33  private hideEventId: number = -1;
34  @State private showControl: boolean = false;
35  private xComponentController: XComponentController = new XComponentController();
36  private surfaceId: string = '';
37  private controlTransEffect: TransitionEffect = TransitionEffect.OPACITY;
38  @Provide @Watch('onHideControlNow') hideControlNow: boolean = false;
39  @Provide @Watch('onHideControlDelay') hideControlDelay: boolean = false;
40
41  onHideControlNow() {
42    if (this.hideControlNow) {
43      this.switchToHideWithoutAnime();
44    }
45    this.hideControlNow = false;
46  }
47
48  onHideControlDelay() {
49    if (this.hideControlDelay) {
50      this.delayHide();
51    }
52    this.hideControlDelay = false;
53  }
54
55  switchToShow() {
56    animateTo({ curve: curves.responsiveSpringMotion(0.25, 1) }, () => {
57      this.showControl = true;
58    });
59    this.delayHide();
60  }
61
62  switchToHide() {
63    if (this.hideEventId !== -1) {
64      clearTimeout(this.hideEventId);
65    }
66    animateTo({ curve: curves.responsiveSpringMotion(0.25, 1) }, () => {
67      this.showControl = false;
68    });
69  }
70
71  switchToHideWithoutAnime() {
72    if (this.hideEventId !== -1) {
73      clearTimeout(this.hideEventId);
74    }
75    this.showControl = false;
76  }
77
78  delayHide() {
79    if (this.hideEventId !== -1) {
80      clearTimeout(this.hideEventId);
81    }
82    this.hideEventId = this.showControl ?
83    setTimeout(() => {
84      animateTo({ curve: curves.responsiveSpringMotion(0.25, 1) }, () => {
85        this.showControl = false;
86      });
87    }, TIMEOUT) :
88      -1;
89  }
90
91  build() {
92    Stack() {
93      XComponent({ id: this.xComponentId, type: 'surface', controller: this.xComponentController })
94        .onLoad(() => {
95          pip.initXComponentController(this.xComponentController);
96          console.debug(TAG, 'XComponent onLoad done');
97        })
98        .fillSize();
99
100      RelativeContainer() {
101        Stack()
102          .fillSize()
103          .id('fill_stack')
104        if (this.showControl) {
105          RelativeContainer() {
106            DefaultControl();
107            MeetingControl();
108          }
109          .fillSize()
110          .transition(this.controlTransEffect)
111          .alignRules({
112            top: { anchor: '__container__', align: VerticalAlign.Top },
113            right: { anchor: '__container__', align: HorizontalAlign.End }
114          })
115          .id('control_inner')
116        }
117      }
118      .fillSize()
119      .id('control')
120      .gesture(
121        GestureGroup(GestureMode.Exclusive,
122          TapGesture({ count: 2 })
123            .onAction((event: GestureEvent) => {
124              this.switchToHideWithoutAnime();
125              pip.processScale();
126            }),
127          TapGesture({ count: 1 })
128            .onAction((event: GestureEvent) => {
129              if (this.showControl) {
130                this.switchToHide();
131              } else {
132                this.switchToShow();
133              }
134            }),
135          PanGesture()
136            .onActionStart((event: GestureEvent) => {
137              this.switchToHide();
138              pip.startMove();
139            })
140        )
141      )
142    }
143    .fillSize()
144  }
145}
146
147@Component
148struct DefaultControl {
149  @Consume hideControlNow: boolean;
150
151  build() {
152    RelativeContainer() {
153      Button({ type: ButtonType.Circle }) {
154        Image($r('sys.media.ohos_ic_public_close'))
155          .size({ width: 24, height: 24 })
156          .fillColor($r('sys.color.ohos_id_color_primary_contrary'))
157          .objectFit(ImageFit.Contain)
158      }
159      .backgroundColor('#00FFFFFF')
160      .size({ width: 24, height: 24 })
161      .margin(12)
162      .alignRules({
163        center: { anchor: '__container__', align: VerticalAlign.Center },
164        left: { anchor: '__container__', align: HorizontalAlign.Start }
165      })
166      .id('control_exit')
167      .responseRegion({ x: '-50%', y: '-50%', width: '200%', height: '200%' })
168      .onClick(() => {
169        this.hideControlNow = true;
170        pip.close();
171        console.debug(TAG, 'action: exit');
172      })
173
174      Button({ type: ButtonType.Circle }) {
175        Image($r('sys.media.ohos_ic_public_restore'))
176          .fillColor($r('sys.color.ohos_id_color_primary_contrary'))
177          .objectFit(ImageFit.Contain)
178      }
179      .backgroundColor('#00FFFFFF')
180      .size({ width: 24, height: 24 })
181      .margin(12)
182      .alignRules({
183        center: { anchor: '__container__', align: VerticalAlign.Center },
184        right: { anchor: '__container__', align: HorizontalAlign.End }
185      })
186      .id('control_restore')
187      .responseRegion({ x: '-50%', y: '-50%', width: '200%', height: '200%' })
188      .onClick(() => {
189        this.hideControlNow = true;
190        pip.restore();
191        console.debug(TAG, 'action: restore');
192      })
193    }
194    .width('100%')
195    .height(48)
196    .linearGradient({
197      angle: 180,
198      colors: [['#30000000', 0.0], ['#00000000', 1.0]]
199    })
200    .alignRules({
201      top: { anchor: '__container__', align: VerticalAlign.Top },
202      left: { anchor: '__container__', align: HorizontalAlign.Start }
203    })
204    .id('default_control')
205  }
206}
207
208const sizeArray = [1.0, 1.5];
209
210@Component
211struct MeetingControl {
212  @State isMute: boolean = true;
213  @State isRecord: boolean = true;
214  @State defaultMargin: number = 8;
215  @State defaultSize: number = 12;
216  @State defaultBigSize: number = 24;
217  @State sizeIndex: number = 0;
218  @Consume hideControlDelay: boolean;
219
220  build() {
221    RelativeContainer() {
222
223      Button({ type: ButtonType.Circle }) {
224        Image($r('sys.media.ohos_ic_public_hang_up'))
225          .size({ width: 12 * sizeArray[this.sizeIndex], height: 12 * sizeArray[this.sizeIndex] })
226          .fillColor($r('sys.color.ohos_id_color_primary_contrary'))
227          .objectFit(ImageFit.Contain)
228      }
229      .backgroundColor($r('sys.color.ohos_id_color_handup'))
230      .size({ width: 24 * sizeArray[this.sizeIndex], height: 24 * sizeArray[this.sizeIndex] })
231      .margin({ top: 12, bottom: 12 })
232      .alignRules({
233        bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
234        middle: { anchor: '__container__', align: HorizontalAlign.Center }
235      })
236      .id('control_hangup')
237      .onClick(() => {
238        this.hideControlDelay = true;
239        pip.triggerAction('hangUp');
240        console.debug(TAG, 'action: hangup');
241      })
242
243      Button({ type: ButtonType.Circle }) {
244        Image(this.isMute ? $r('sys.media.ohos_ic_public_sound_off') : $r('sys.media.ohos_ic_public_sound'))
245          .size({ width: 8 * sizeArray[this.sizeIndex], height: 8 * sizeArray[this.sizeIndex] })
246          .fillColor($r('sys.color.ohos_id_color_primary'))
247          .objectFit(ImageFit.Contain)
248      }
249      .backgroundColor($r('sys.color.ohos_id_color_floating_button_icon'))
250      .size({ width: 16 * sizeArray[this.sizeIndex], height: 16 * sizeArray[this.sizeIndex] })
251      .margin({ left: 8 * sizeArray[this.sizeIndex], right: 8 * sizeArray[this.sizeIndex] })
252      .alignRules({
253        center: { anchor: 'control_hangup', align: VerticalAlign.Center },
254        right: { anchor: 'control_hangup', align: HorizontalAlign.Start }
255      })
256      .id('control_mute')
257      .onClick(() => {
258        this.hideControlDelay = true;
259        this.isMute = !this.isMute;
260        pip.triggerAction('voiceStateChanged');
261        console.debug(TAG, 'action: voice enable or disable');
262      })
263
264      Button({ type: ButtonType.Circle }) {
265        Image(this.isRecord ? $r('sys.media.ohos_ic_public_video') : $r('sys.media.ohos_ic_public_video_off'))
266          .size({ width: 8 * sizeArray[this.sizeIndex], height: 8 * sizeArray[this.sizeIndex] })
267          .fillColor($r('sys.color.ohos_id_color_primary'))
268          .objectFit(ImageFit.Contain)
269      }
270      .backgroundColor($r('sys.color.ohos_id_color_floating_button_icon'))
271      .size({ width: 16 * sizeArray[this.sizeIndex], height: 16 * sizeArray[this.sizeIndex] })
272      .margin({ left: 8 * sizeArray[this.sizeIndex], right: 8 * sizeArray[this.sizeIndex] })
273      .alignRules({
274        center: { anchor: 'control_hangup', align: VerticalAlign.Center },
275        left: { anchor: 'control_hangup', align: HorizontalAlign.End }
276      })
277      .id('control_record')
278      .onClick(() => {
279        this.hideControlDelay = true;
280        this.isRecord = !this.isRecord;
281        pip.triggerAction('videoStateChanged');
282        console.debug(TAG, 'action: video enable or disable');
283      })
284    }
285    .width('100%')
286    .height(48)
287    .onAreaChange((oldValue: Area, newValue: Area) => {
288      if (oldValue.width == newValue.width) {
289        return;
290      }
291      this.sizeIndex = newValue.width >= 150 ? 1 : 0;
292    })
293    .alignRules({
294      bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
295      left: { anchor: '__container__', align: HorizontalAlign.Start }
296    })
297    .id('meeting_control')
298  }
299}
300
301