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}