• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2025 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 { NetEventData, NetUtils, NetworkEventName } from '../utils/NetUtils';
17import { emitter } from '@kit.BasicServicesKit';
18import { connection } from '@kit.NetworkKit';
19import { logger } from '../utils/Logger';
20import { Prompt } from '@kit.ArkUI';
21import { SettingPage } from './SettingPage';
22
23// 将自动播放设置通过PersistentStorage进行本地持久化存储,避免每次打开应用都需要重新设置
24PersistentStorage.persistProp('cellular_auto_play', false);
25PersistentStorage.persistProp('wifi_auto_play', false);
26const innerEvent: emitter.InnerEvent = {
27  // 左上角返回按钮点击事件传递的eventId
28  eventId: 6
29};
30
31/**
32 * 实现步骤:
33 * 1. 通过@kit.NetworkKit接口监听网络状态
34 * 2. 添加Video组件,播放在线视频
35 * 3. 添加自动播放设置toggle,可以在网络状态变化时修改视频播放状态
36 */
37
38@Component
39export struct VideoPage {
40  @Provide('navPathStack') navPathStack: NavPathStack = new NavPathStack();
41  // 视频控制器
42  controller: VideoController = new VideoController();
43  // WI-FI自动播放
44  @StorageLink('wifi_auto_play') wifiAutoPlay: boolean = false;
45  // 3G/4G/5G自动播放
46  @StorageLink('cellular_auto_play') cellularAutoPlay: boolean = false;
47  // 在线视频地址
48  private videoUrl: string = 'https://v.oh4k.com/muhou/2022/07/20220704-RIUq3Z.mp4';
49  // 注册路由返回函数,案例插件不触发
50  popRouter: () => void = () => {
51  };
52  // 使用流量播放弹窗
53  networkDialog: CustomDialogController | null = new CustomDialogController({
54    builder: NetworkDialogComponent({
55      title: $r('app.string.network_status_observer_cellular_dialog_title'),
56      message: $r('app.string.network_status_observer_cellular_dialog_message'),
57      cancel: () => {
58        // 用户点击取消,则停止播放
59        this.pausePlay();
60        this.networkDialog?.close();
61      },
62      confirm: () => {
63        // 用户点击确认,则继续播放
64        this.startPlay();
65        this.networkDialog?.close();
66      }
67    }),
68    cornerRadius: $r('app.integer.network_status_observer_cellular_dialog_message_radius'),
69    alignment: DialogAlignment.Center
70  })
71
72  // 网络监听回调
73  netObserver(data: emitter.EventData) {
74    if (!data.data) {
75      logger.info('netObserver data.data is undefined.');
76      return;
77    }
78    logger.info('network observe result : ' + NetUtils.getInstance().parseResult(data));
79    let netEventData: NetEventData = data.data! as NetEventData;
80    let eventName: NetworkEventName = netEventData.eventName ?? -1;
81    switch (eventName) {
82      case NetworkEventName.NetAvailable:
83        // WI-FI是可用状态
84        if (netEventData.netType === connection.NetBearType.BEARER_WIFI) {
85          // 如果开了WI-FI自动播放,则继续播放
86          if (this.wifiAutoPlay) {
87            this.startPlay();
88          }
89        }
90        break;
91      case NetworkEventName.NetBlock:
92        break;
93      case NetworkEventName.NetLost:
94        // 如果WI-FI网络丢失,则通过wifiInterrupt方法判断是否需要继续播放
95        if (netEventData.netType === connection.NetBearType.BEARER_WIFI) {
96          this.wifiInterrupt();
97        }
98        break;
99      case NetworkEventName.NetUnavailable:
100        // 如果WI-FI不可用,则通过wifiInterrupt方法判断是否需要继续播放
101        if (netEventData.netType === connection.NetBearType.BEARER_WIFI) {
102          this.wifiInterrupt();
103        }
104        break;
105      case NetworkEventName.WeakNet:
106        // 如果是弱网环境,则弹出提示,实际应用开发中可以通过该结果自动实现分辨率自动切换
107        if (netEventData.status) {
108          Prompt.showToast({
109            message: getContext().resourceManager.getStringSync($r('app.string.network_status_observer_weak'))
110          });
111        }
112        break;
113      default:
114        logger.debug('当前网络状态:' + eventName);
115        break;
116    }
117  }
118
119  /**
120   * WI-FI中断时的操作
121   * 如果开启了3G/4G/5G自动播放,则继续播放,并且提示正在使用流量播放
122   * 如果关闭了3G/4G/5G自动播放,则弹出弹窗,让用户选择是否继续使用流量播放
123   */
124  wifiInterrupt() {
125    if (NetUtils.getInstance().getNetworkConnectionType()[0] === connection.NetBearType.BEARER_CELLULAR) {
126      if (this.cellularAutoPlay) {
127        Prompt.showToast({
128          message: getContext().resourceManager.getStringSync($r('app.string.network_status_observer_user_cellular'))
129        });
130      } else {
131        this.pausePlay();
132        this.networkDialog?.open();
133      }
134    }
135  }
136
137  /**
138   * 是否自动播放
139   * @returns true:自动播放,false,不自动播放
140   */
141  autoPlay(): boolean {
142    let autoPlay: boolean = false;
143    // 如果网络是可用的
144    if (NetUtils.getInstance().judgeHasNet()) {
145      // 获取当前连接的网络类型
146      let currentNetType: connection.NetBearType = NetUtils.getInstance().getNetworkConnectionType()[0];
147      switch (currentNetType) {
148        case connection.NetBearType.BEARER_CELLULAR: // 蜂窝网络
149          // 如果开启了3G/4G/5G自动播放,则设置autoPlay为true
150          if (this.cellularAutoPlay) {
151            autoPlay = true;
152          }
153          break;
154        case connection.NetBearType.BEARER_WIFI: // WIFI网络
155        case connection.NetBearType.BEARER_ETHERNET: // 以太网网络(模拟器)
156          // 如果设置了WI-FI自动播放,则设置autoPlay为true
157          if (this.wifiAutoPlay) {
158            autoPlay = true;
159          }
160          break;
161      }
162    }
163    return autoPlay;
164  }
165
166  // 开始播放
167  startPlay() {
168    if (this.controller) {
169      this.controller.start();
170    }
171  }
172
173  // 暂停播放
174  pausePlay() {
175    if (this.controller) {
176      this.controller.pause();
177    }
178  }
179
180  onPageShow(): void {
181    if (this.autoPlay()) {
182      this.startPlay();
183    }
184  }
185
186  onPageHide(): void {
187    this.pausePlay();
188  }
189
190  aboutToAppear(): void {
191    // 通过emitter接受网络监听结果
192    emitter.on(NetUtils.getInstance().getEmitterEvent(), (data: emitter.EventData) => {
193      if (data) {
194        this.netObserver(data);
195      } else {
196        logger.info('aboutToAppear emitter on error, data is undefined.');
197      }
198    });
199    // 开启蜂窝网络和WI-FI网络状态的监听
200    NetUtils.getInstance()
201      .startNetObserve(connection.NetBearType.BEARER_CELLULAR, connection.NetBearType.BEARER_WIFI);
202    // 收到eventId为6的事件后执行回调函数
203    emitter.on(innerEvent, () => {
204      // 在案例主页时,返回瀑布流
205      if (this.navPathStack.size() === 0) {
206        this.popRouter();
207      }
208    });
209  }
210
211  aboutToDisappear(): void {
212    // 当页面销毁时,停止所有网络监听
213    NetUtils.getInstance().stopAllNetObserve();
214    if (this.controller) {
215      this.controller.stop();
216    }
217    // 销毁事件监听
218    emitter.off(innerEvent.eventId);
219  }
220
221  @Builder
222  buildMap(name: string, param: ESObject) {
223    if (name === 'SettingPage') {
224      NavDestination() {
225        SettingPage()
226      }.hideTitleBar(true)
227    }
228  }
229
230  build() {
231    Navigation(this.navPathStack) {
232      Column() {
233        Row() {
234          Text($r('app.string.network_status_observer_auto_play_setting'))
235        }.justifyContent(FlexAlign.End)
236        .width($r('app.string.network_status_observer_percent_100'))
237        .onClick(() => {
238          this.navPathStack.pushPath({ name: 'SettingPage' });
239        })
240
241        Video({
242          src: this.videoUrl,
243          controller: this.controller
244        })
245          .height($r('app.integer.network_status_observer_video_height'))
246          .width($r('app.string.network_status_observer_percent_100'))
247          .autoPlay(this.autoPlay())
248          .id('id_network_status_observer_video')
249      }
250      .height($r('app.string.network_status_observer_percent_100'))
251      .width($r('app.string.network_status_observer_percent_100'))
252    }.hideTitleBar(true)
253    .hideToolBar(true)
254    .navDestination(this.buildMap)
255
256  }
257}
258
259// 流量播放提示框
260@CustomDialog
261export struct NetworkDialogComponent {
262  controller?: CustomDialogController;
263  // 标题
264  title: ResourceStr = '';
265  // 提示信息
266  message: ResourceStr = '';
267  // 取消事件
268  cancel: () => void = () => {
269  };
270  // 确认事件
271  confirm: () => void = () => {
272  };
273
274  build() {
275    Column() {
276      Text(this.title)
277        .fontSize($r('app.integer.network_status_observer_cellular_dialog_title_font_size'))
278        .fontWeight(FontWeight.Bold)
279      Text(this.message)
280        .padding({
281          left: $r('app.integer.network_status_observer_cellular_dialog_message_padding'),
282          right: $r('app.integer.network_status_observer_cellular_dialog_message_padding')
283        })
284        .margin({
285          top: $r('app.integer.network_status_observer_cellular_dialog_message_margin_top')
286        })
287      Line().height(1)
288        .backgroundColor(Color.Blue)
289      Row() {
290        Button($r('app.string.network_status_observer_dialog_cancel'))
291          .layoutWeight(1)
292          .borderRadius({
293            bottomRight: 0,
294            topLeft: 0,
295            topRight: 0,
296            bottomLeft: $r('app.integer.network_status_observer_cellular_dialog_message_radius')
297          })
298          .type(ButtonType.Normal)
299          .backgroundColor(Color.White)
300          .fontColor(Color.Grey)
301          .onClick(() => {
302            this.cancel();
303          })
304        Line().width(1)
305          .backgroundColor(Color.Blue)
306        Button($r('app.string.network_status_observer_dialog_confirm'))
307          .layoutWeight(1)
308          .borderRadius({
309            bottomRight: $r('app.integer.network_status_observer_cellular_dialog_message_radius'),
310            topLeft: 0,
311            topRight: 0,
312            bottomLeft: 0
313          })
314          .type(ButtonType.Normal)
315          .backgroundColor(Color.White)
316          .fontColor(Color.Blue)
317          .onClick(() => {
318            this.confirm();
319          })
320      }.margin({
321        top: $r('app.integer.network_status_observer_cellular_dialog_button_margin_top')
322      })
323    }
324    .padding({
325      top: $r('app.integer.network_status_observer_cellular_dialog_padding_top')
326    })
327    .alignItems(HorizontalAlign.Center)
328    .backgroundColor(Color.White)
329  }
330}