fanmingyou3_wd

添加播放器模块,及短视频详情播放模块

Showing 132 changed files with 3104 additions and 28 deletions
  1 +export interface AuthorListDTO {
  2 + authorName: string;
  3 +}
  1 +import { AuthorListDTO } from './AuthorListDTO';
  2 +import { FullColumnImgUrlDTO } from './FullColumnImgUrlDTO';
  3 +import { ReLInfoDTO } from './ReLInfoDTO';
  4 +import { ShareInfoDTO } from './ShareInfoDTO';
  5 +import { VideoInfoDTO } from './VideoInfoDTO';
  6 +
  7 +/**
  8 + * 接口定义:
  9 + * http://192.168.1.3:3300/project/3802/interface/api/200915
  10 + */
  11 +export interface ContentDetailDTO {
  12 + newsId: string;
  13 + newsTitle: string;
  14 + newsShortTitle: string;
  15 + newsDownTitle: string;
  16 + newsBodyTitle: string;
  17 + publishTime: string;
  18 + appstyle: number;
  19 + newsType: number;
  20 + newsSummary: string;
  21 + newsSource: string;
  22 + newsSourceName: string;
  23 + newsContent: string;
  24 + newsContentBak: string;
  25 + newsLinkUrl: string;
  26 + bestNoticer: number;
  27 + newLinkObject?: any;
  28 + newIntroduction: string;
  29 + authorList: AuthorListDTO[];
  30 + editorName: string;
  31 + openAudio: number;
  32 + audioList: any[];
  33 + hasPopUp?: any;
  34 + popUps?: any[];
  35 + firstFrameImageUri: string;
  36 + reLInfo?: ReLInfoDTO;
  37 + fullColumnImgUrls: FullColumnImgUrlDTO[];
  38 + shareInfo: ShareInfoDTO;
  39 + photoList: any[];
  40 + videoInfo: VideoInfoDTO[];
  41 + liveInfo?: any;
  42 + voteInfo?: any;
  43 + rmhInfo?: any;
  44 + userInfo?: any;
  45 + openLikes: number;
  46 + openComment: number;
  47 + likesStyle: number;
  48 + preCommentFlag: number;
  49 + commentDisplay: number;
  50 + keyArticle: number;
  51 + rmhPlatform: number;
  52 + readFlag?: number;
  53 + topicInfo?: any;
  54 + traceId: string;
  55 + itemId: string;
  56 + sceneId: string;
  57 + subSceneId: string;
  58 + activityInfos: any[];
  59 + recommendShow: number;
  60 + visitorComment: number;
  61 + itemTypeCode: string;
  62 + menuShow: number;
  63 + newsTags: string;
  64 + specialColumnId?: any;
  65 + specialColumnName: string;
  66 + timeline?: any;
  67 + traceInfo: string;
  68 + viewCount: number;
  69 +}
  1 +export interface FullColumnImgUrlDTO {
  2 + format?: any;
  3 + height: number;
  4 + landscape: number;
  5 + size: number;
  6 + url: string;
  7 + weight: number;
  8 +}
  1 +export interface ReLInfoDTO {
  2 + channelId: number;
  3 + relId: string;
  4 + relObjectId: number;
  5 + relType: string;
  6 +}
  1 +export interface ShareInfoDTO {
  2 + shareCoverUrl: string;
  3 + shareOpen: number;
  4 + sharePosterCoverUrl: string;
  5 + sharePosterOpen: number;
  6 + shareSummary: string;
  7 + shareTitle: string;
  8 + shareUrl: string;
  9 +}
  1 +export interface VideoInfoDTO {
  2 + clarity: number;
  3 + resolutionHeight: number;
  4 + resolutionWidth: number;
  5 + videoDuration: number;
  6 + videoLandScape: number;
  7 + videoType: number;
  8 + videoUrl: string;
  9 +}
@@ -5,13 +5,13 @@ @@ -5,13 +5,13 @@
5 "name": "default", 5 "name": "default",
6 "type": "HarmonyOS", 6 "type": "HarmonyOS",
7 "material": { 7 "material": {
8 - "certpath": "C:\\Users\\PC\\.ohos\\config\\auto_debug_sight_harmony_com.wondertek.sight_70086000327424393.cer",  
9 - "storePassword": "0000001B4100D63EDF7155D2954BDDEC8F40FA74E710B0D1FF3C0782DE2745F90F1B83D1C1C110398F4559", 8 + "certpath": "C:\\Users\\PC\\.ohos\\config\\auto_debug_sight_harmony_com.wondertek.sight_70086000309521319.cer",
  9 + "storePassword": "0000001B264B065AE68D1C7F3C0863A33C083D91E12CC54AA36D44179AA8DBA37EA3C50E7F3692F5EB6F3E",
10 "keyAlias": "debugKey", 10 "keyAlias": "debugKey",
11 - "keyPassword": "0000001B157D6824BE8F21F112459AD1B61654DE9396A50B0CE60898F02F4C95CBF127122F293DD6C80C62",  
12 - "profile": "C:\\Users\\PC\\.ohos\\config\\auto_debug_sight_harmony_com.wondertek.sight_70086000327424393.p7b", 11 + "keyPassword": "0000001BFB62A8007F44B0EAAF9CF878A92620ED75A21E53B3740EA60DEBF6543F6E16AA7200542280D746",
  12 + "profile": "C:\\Users\\PC\\.ohos\\config\\auto_debug_sight_harmony_com.wondertek.sight_70086000309521319.p7b",
13 "signAlg": "SHA256withECDSA", 13 "signAlg": "SHA256withECDSA",
14 - "storeFile": "C:\\Users\\PC\\.ohos\\config\\auto_debug_sight_harmony_com.wondertek.sight_70086000327424393.p12" 14 + "storeFile": "C:\\Users\\PC\\.ohos\\config\\auto_debug_sight_harmony_com.wondertek.sight_70086000309521319.p12"
15 } 15 }
16 } 16 }
17 ], 17 ],
@@ -133,7 +133,7 @@ @@ -133,7 +133,7 @@
133 ] 133 ]
134 } 134 }
135 ] 135 ]
136 - } 136 + },
137 // { 137 // {
138 // "name": "wdLayout", 138 // "name": "wdLayout",
139 // "srcPath": "./wdLayout", 139 // "srcPath": "./wdLayout",
@@ -145,6 +145,66 @@ @@ -145,6 +145,66 @@
145 // ] 145 // ]
146 // } 146 // }
147 // ] 147 // ]
148 - // } 148 + // },
  149 + {
  150 + "name": "wdPlayer",
  151 + "srcPath": "./wdPlayer",
  152 + "targets": [
  153 + {
  154 + "name": "default",
  155 + "applyToProducts": [
  156 + "default"
  157 + ]
  158 + }
  159 + ]
  160 + },
  161 + {
  162 + "name": "wdDetailPlayApi",
  163 + "srcPath": "./wdDetailPlayApi",
  164 + "targets": [
  165 + {
  166 + "name": "default",
  167 + "applyToProducts": [
  168 + "default"
  169 + ]
  170 + }
  171 + ]
  172 + },
  173 + {
  174 + "name": "wdDetailPlayShortVideo",
  175 + "srcPath": "./wdDetailPlayShortVideo",
  176 + "targets": [
  177 + {
  178 + "name": "default",
  179 + "applyToProducts": [
  180 + "default"
  181 + ]
  182 + }
  183 + ]
  184 + },
  185 + {
  186 + "name": "wdDetailPlayVod",
  187 + "srcPath": "./wdDetailPlayVod",
  188 + "targets": [
  189 + {
  190 + "name": "default",
  191 + "applyToProducts": [
  192 + "default"
  193 + ]
  194 + }
  195 + ]
  196 + },
  197 + {
  198 + "name": "wdDetailPlayLive",
  199 + "srcPath": "./wdDetailPlayLive",
  200 + "targets": [
  201 + {
  202 + "name": "default",
  203 + "applyToProducts": [
  204 + "default"
  205 + ]
  206 + }
  207 + ]
  208 + }
149 ] 209 ]
150 } 210 }
@@ -4,6 +4,7 @@ import UIAbility from '@ohos.app.ability.UIAbility'; @@ -4,6 +4,7 @@ import UIAbility from '@ohos.app.ability.UIAbility';
4 import Want from '@ohos.app.ability.Want'; 4 import Want from '@ohos.app.ability.Want';
5 import window from '@ohos.window'; 5 import window from '@ohos.window';
6 import { registerRouter } from 'wdRouter'; 6 import { registerRouter } from 'wdRouter';
  7 +import { WindowModel } from 'wdKit';
7 8
8 export default class EntryAbility extends UIAbility { 9 export default class EntryAbility extends UIAbility {
9 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { 10 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
@@ -18,7 +19,16 @@ export default class EntryAbility extends UIAbility { @@ -18,7 +19,16 @@ export default class EntryAbility extends UIAbility {
18 onWindowStageCreate(windowStage: window.WindowStage): void { 19 onWindowStageCreate(windowStage: window.WindowStage): void {
19 // Main window is created, set main page for this ability 20 // Main window is created, set main page for this ability
20 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 21 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
21 - 22 + WindowModel.shared.setWindowStage(windowStage);
  23 + // let a = new WindowModel();
  24 + // 设置窗口的显示方向属性
  25 + WindowModel.shared.setPreferredOrientation(window.Orientation.PORTRAIT)
  26 + .then(() => {
  27 + hilog.info(0x0000, 'testTag', 'setPreferredOrientation Succeeded');
  28 + })
  29 + .catch((err: Error) => {
  30 + hilog.error(0x0000, 'testTag', `setPreferredOrientation catch, error error.name : ${err.name}, error.message:${err.message}`);
  31 + })
22 windowStage.loadContent('pages/Index', (err, data) => { 32 windowStage.loadContent('pages/Index', (err, data) => {
23 if (err.code) { 33 if (err.code) {
24 hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 34 hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
  1 +{
  2 + "code":"0",
  3 + "data":[
  4 + {
  5 + "activityInfos":[
  6 +
  7 + ],
  8 + "appstyle":13,
  9 + "audioList":[
  10 +
  11 + ],
  12 + "authorList":[
  13 + {
  14 + "authorName":""
  15 + }
  16 + ],
  17 + "bestNoticer":1,
  18 + "commentDisplay":1,
  19 + "editorName":"韩文鋆",
  20 + "firstFrameImageUri":"https://cdnjdout.aikan.pdnews.cn/zhbj-20240127/vod/content/output/f45ef51bb33f4ffd9458f8b386aa3227_opt.png",
  21 + "fullColumnImgUrls":[
  22 + {
  23 + "format":null,
  24 + "height":837,
  25 + "landscape":1,
  26 + "size":1222753,
  27 + "url":"https://cdnjdphoto.aikan.pdnews.cn/sjbj-20240127/image/display/efd5771a861f45dd8170da1c3c8c4d04.png",
  28 + "weight":1256
  29 + }
  30 + ],
  31 + "hasPopUp":null,
  32 + "itemId":"",
  33 + "itemTypeCode":"",
  34 + "keyArticle":0,
  35 + "likesStyle":1,
  36 + "liveInfo":null,
  37 + "menuShow":1,
  38 + "newIntroduction":"",
  39 + "newLinkObject":null,
  40 + "newsBodyTitle":"",
  41 + "newsContent":"",
  42 + "newsContentBak":"",
  43 + "newsDownTitle":"",
  44 + "newsId":30013266075,
  45 + "newsLinkUrl":"",
  46 + "newsShortTitle":"",
  47 + "newsSource":"41",
  48 + "newsSourceName":"中国铁路微信公号",
  49 + "newsSummary":"",
  50 + "newsTags":"",
  51 + "newsTitle":"旅途平安!这首歌送给即将启程回家的你",
  52 + "newsType":1,
  53 + "openAudio":1,
  54 + "openComment":1,
  55 + "openLikes":1,
  56 + "photoList":[
  57 +
  58 + ],
  59 + "popUps":[
  60 +
  61 + ],
  62 + "preCommentFlag":1,
  63 + "publishTime":"2024-01-27 14:18:52",
  64 + "reLInfo":{
  65 + "channelId":2002,
  66 + "relId":"500000301942",
  67 + "relObjectId":2002,
  68 + "relType":"1"
  69 + },
  70 + "readFlag":0,
  71 + "recommendShow":1,
  72 + "rmhInfo":null,
  73 + "rmhPlatform":0,
  74 + "sceneId":"",
  75 + "shareInfo":{
  76 + "shareCoverUrl":"https://cdnjdphoto.aikan.pdnews.cn/zhbj-20240127/image/content/6706775f96a346a8a1e7393c325e043d.png?x-oss-process=image/resize,w_200",
  77 + "shareOpen":1,
  78 + "sharePosterCoverUrl":"https://cdnjdphoto.aikan.pdnews.cn/sjbj-20240127/image/display/efd5771a861f45dd8170da1c3c8c4d04.png",
  79 + "sharePosterOpen":1,
  80 + "shareSummary":"人民日报,有品质的新闻",
  81 + "shareTitle":"旅途平安!这首歌送给即将启程回家的你",
  82 + "shareUrl":"https://people.pdnews.cn/vod/rel/500000301942/30013266075"
  83 + },
  84 + "specialColumnId":null,
  85 + "specialColumnName":"",
  86 + "subSceneId":"",
  87 + "timeline":null,
  88 + "topicInfo":null,
  89 + "traceId":"",
  90 + "traceInfo":"",
  91 + "userInfo":null,
  92 + "videoInfo":[
  93 + {
  94 + "clarity":5,
  95 + "resolutionHeight":1080,
  96 + "resolutionWidth":1920,
  97 + "videoDuration":143,
  98 + "videoLandScape":1,
  99 + "videoType":1,
  100 + "videoUrl":"https://cdnjdout.aikan.pdnews.cn/zhbj-20240127/vod/content/output/f45ef51bb33f4ffd9458f8b386aa3227_opt.mp4"
  101 + }
  102 + ],
  103 + "viewCount":0,
  104 + "voteInfo":null
  105 + }
  106 + ],
  107 + "message":"Success",
  108 + "meta":null,
  109 + "requestId":"",
  110 + "success":true,
  111 + "timestamp":1706514747211
  112 +}
@@ -30,6 +30,8 @@ export { Action } from './src/main/ets/bean/programme/Action' @@ -30,6 +30,8 @@ export { Action } from './src/main/ets/bean/programme/Action'
30 30
31 export { Params } from './src/main/ets/bean/programme/Params' 31 export { Params } from './src/main/ets/bean/programme/Params'
32 32
  33 +export { Pic } from './src/main/ets/bean/programme/Pic'
  34 +
33 export { LabelBean } from './src/main/ets/bean/component/extra/LabelBean'; 35 export { LabelBean } from './src/main/ets/bean/component/extra/LabelBean';
34 36
35 export { LabelDTO } from './src/main/ets/bean/component/extra/LabelDTO'; 37 export { LabelDTO } from './src/main/ets/bean/component/extra/LabelDTO';
@@ -48,6 +50,6 @@ export { NewspaperPositionItemBean } from './src/main/ets/bean/newspaper/Newspap @@ -48,6 +50,6 @@ export { NewspaperPositionItemBean } from './src/main/ets/bean/newspaper/Newspap
48 50
49 export { NewspaperShareBean } from './src/main/ets/bean/newspaper/NewspaperShareBean'; 51 export { NewspaperShareBean } from './src/main/ets/bean/newspaper/NewspaperShareBean';
50 52
51 -export { NewspaperTimeInfoBean} from './src/main/ets/bean/newspaper/NewspaperTimeInfoBean'; 53 +export { NewspaperTimeInfoBean } from './src/main/ets/bean/newspaper/NewspaperTimeInfoBean';
52 54
53 export { NewspaperTimeItemBean } from './src/main/ets/bean/newspaper/NewspaperTimeItemBean'; 55 export { NewspaperTimeItemBean } from './src/main/ets/bean/newspaper/NewspaperTimeItemBean';
@@ -6,5 +6,15 @@ export interface Params { @@ -6,5 +6,15 @@ export interface Params {
6 path?: string; 6 path?: string;
7 url?: string; 7 url?: string;
8 extra?: ExtraDTO; // 跳转时额外需要带的参数:map<String,String> 即仅有一层的json 8 extra?: ExtraDTO; // 跳转时额外需要带的参数:map<String,String> 即仅有一层的json
  9 +
  10 + // 详情页类型
  11 + // 1.点播详情页
  12 + // 2.直播详情页
  13 + // 3.图文详情页
  14 + // 4.全民播详情页
  15 + // 5.欢喜详情页
  16 + // 6.挂件详情页
  17 + // 7.沉浸式竖屏详情页
  18 + // 8.专辑竖屏详情页
9 detailPageType?:number; // 详情页类型 19 detailPageType?:number; // 详情页类型
10 } 20 }
1 import { CompDTO, ContentDTO, DelayTimeEnum } from 'wdBean'; 1 import { CompDTO, ContentDTO, DelayTimeEnum } from 'wdBean';
2 -import { BreakpointConstants, CommonConstants } from 'wdConstant'; 2 +import { BreakpointConstants } from 'wdConstant';
3 import { BreakPointType, Logger } from 'wdKit'; 3 import { BreakPointType, Logger } from 'wdKit';
4 -import { CompUtils } from '../utils/CompUtils';  
5 import { CarouselLayout01CardView } from './CardView'; 4 import { CarouselLayout01CardView } from './CardView';
6 import { EmptyComponent } from './EmptyComponent'; 5 import { EmptyComponent } from './EmptyComponent';
7 6
@@ -71,10 +71,19 @@ export struct CarouselLayout01CardView { @@ -71,10 +71,19 @@ export struct CarouselLayout01CardView {
71 .hoverEffect(HoverEffect.Scale) 71 .hoverEffect(HoverEffect.Scale)
72 .onClick((event: ClickEvent) => { 72 .onClick((event: ClickEvent) => {
73 Logger.info(TAG, `BannerComponent onClick event index: ${this.index}`); 73 Logger.info(TAG, `BannerComponent onClick event index: ${this.index}`);
  74 + // let taskAction: Action = {
  75 + // type: 'JUMP_H5_BY_WEB_VIEW',
  76 + // params: {
  77 + // url: ConfigConstants.DETAIL_URL
  78 + // } as Params,
  79 + // };
  80 + // WDRouterRule.jumpWithAction(taskAction)
  81 +
74 let taskAction: Action = { 82 let taskAction: Action = {
75 - type: 'JUMP_H5_BY_WEB_VIEW', 83 + type: 'JUMP_DETAIL_PAGE',
76 params: { 84 params: {
77 - url: ConfigConstants.DETAIL_URL 85 + detailPageType: 7, // 沉浸式竖屏详情页
  86 + contentID: '863556812'
78 } as Params, 87 } as Params,
79 }; 88 };
80 WDRouterRule.jumpWithAction(taskAction) 89 WDRouterRule.jumpWithAction(taskAction)
  1 +/node_modules
  2 +/oh_modules
  3 +/.preview
  4 +/build
  5 +/.cxx
  6 +/.test
  1 +export { ContentDetailRequest, ContentDetailRequestParams } from './src/main/ets/request/ContentDetailRequest'
  2 +
  3 +// export { PlaySetting } from './src/main/ets/utils/PlaySetting'
  4 +export { PlaySpeedDialog, } from './src/main/ets/view/PlayDialog'
  5 +
  6 +export { PlayError } from './src/main/ets/view/PlayError'
  7 +
  8 +export { devicePLSensorManager } from './src/main/ets/utils/devicePortLandSensor'
  9 +
  10 +
  1 +{
  2 + "apiType": "stageMode",
  3 + "buildOption": {
  4 + "arkOptions": {
  5 + // "apPath": "./modules.ap" /* Profile used for profile-guided optimization (PGO), a compiler optimization technique to improve app runtime performance. */
  6 + }
  7 + },
  8 + "buildOptionSet": [
  9 + {
  10 + "name": "release",
  11 + "arkOptions": {
  12 + "obfuscation": {
  13 + "ruleOptions": {
  14 + "enable": true,
  15 + "files": [
  16 + "./obfuscation-rules.txt"
  17 + ]
  18 + }
  19 + }
  20 + }
  21 + },
  22 + ],
  23 + "targets": [
  24 + {
  25 + "name": "default"
  26 + }
  27 + ]
  28 +}
  1 +import { hspTasks } from '@ohos/hvigor-ohos-plugin';
  2 +
  3 +export default {
  4 + system: hspTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
  5 + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
  6 +}
  1 +# Define project specific obfuscation rules here.
  2 +# You can include the obfuscation configuration files in the current module's build-profile.json5.
  3 +#
  4 +# For more details, see
  5 +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
  6 +
  7 +# Obfuscation options:
  8 +# -disable-obfuscation: disable all obfuscations
  9 +# -enable-property-obfuscation: obfuscate the property names
  10 +# -enable-toplevel-obfuscation: obfuscate the names in the global scope
  11 +# -compact: remove unnecessary blank spaces and all line feeds
  12 +# -remove-log: remove all console.* statements
  13 +# -print-namecache: print the name cache that contains the mapping from the old names to new names
  14 +# -apply-namecache: reuse the given cache file
  15 +
  16 +# Keep options:
  17 +# -keep-property-name: specifies property names that you want to keep
  18 +# -keep-global-name: specifies names that you want to keep in the global scope
  1 +{
  2 + "name": "wddetailplayapi",
  3 + "version": "1.0.0",
  4 + "description": "Please describe the basic information.",
  5 + "main": "Index.ets",
  6 + "author": "",
  7 + "license": "Apache-2.0",
  8 + "dependencies": {
  9 + "wdBean": "file:../wdBean",
  10 + "wdKit": "file:../wdKit",
  11 + "wdNetwork": "file:../wdNetwork",
  12 + "wdRouter": "file:../wdRouter"
  13 + }
  14 +}
  1 +export interface AuthorListDTO {
  2 + authorName: string;
  3 +}
  1 +import { AuthorListDTO } from './AuthorListDTO';
  2 +import { FullColumnImgUrlDTO } from './FullColumnImgUrlDTO';
  3 +import { ReLInfoDTO } from './ReLInfoDTO';
  4 +import { ShareInfoDTO } from './ShareInfoDTO';
  5 +import { VideoInfoDTO } from './VideoInfoDTO';
  6 +
  7 +/**
  8 + * 接口定义:
  9 + * http://192.168.1.3:3300/project/3802/interface/api/200915
  10 + */
  11 +export interface ContentDetailDTO {
  12 + newsId: string;
  13 + newsTitle: string;
  14 + newsShortTitle: string;
  15 + newsDownTitle: string;
  16 + newsBodyTitle: string;
  17 + publishTime: string;
  18 + appstyle: number;
  19 + newsType: number;
  20 + newsSummary: string;
  21 + newsSource: string;
  22 + newsSourceName: string;
  23 + newsContent: string;
  24 + newsContentBak: string;
  25 + newsLinkUrl: string;
  26 + bestNoticer: number;
  27 + newLinkObject?: any;
  28 + newIntroduction: string;
  29 + authorList: AuthorListDTO[];
  30 + editorName: string;
  31 + openAudio: number;
  32 + audioList: any[];
  33 + hasPopUp?: any;
  34 + popUps?: any[];
  35 + firstFrameImageUri: string;
  36 + reLInfo?: ReLInfoDTO;
  37 + fullColumnImgUrls: FullColumnImgUrlDTO[];
  38 + shareInfo: ShareInfoDTO;
  39 + photoList: any[];
  40 + videoInfo: VideoInfoDTO[];
  41 + liveInfo?: any;
  42 + voteInfo?: any;
  43 + rmhInfo?: any;
  44 + userInfo?: any;
  45 + openLikes: number;
  46 + openComment: number;
  47 + likesStyle: number;
  48 + preCommentFlag: number;
  49 + commentDisplay: number;
  50 + keyArticle: number;
  51 + rmhPlatform: number;
  52 + readFlag?: number;
  53 + topicInfo?: any;
  54 + traceId: string;
  55 + itemId: string;
  56 + sceneId: string;
  57 + subSceneId: string;
  58 + activityInfos: any[];
  59 + recommendShow: number;
  60 + visitorComment: number;
  61 + itemTypeCode: string;
  62 + menuShow: number;
  63 + newsTags: string;
  64 + specialColumnId?: any;
  65 + specialColumnName: string;
  66 + timeline?: any;
  67 + traceInfo: string;
  68 + viewCount: number;
  69 +}
  1 +export interface FullColumnImgUrlDTO {
  2 + format?: any;
  3 + height: number;
  4 + landscape: number;
  5 + size: number;
  6 + url: string;
  7 + weight: number;
  8 +}
  1 +export interface ReLInfoDTO {
  2 + channelId: number;
  3 + relId: string;
  4 + relObjectId: number;
  5 + relType: string;
  6 +}
  1 +export interface ShareInfoDTO {
  2 + shareCoverUrl: string;
  3 + shareOpen: number;
  4 + sharePosterCoverUrl: string;
  5 + sharePosterOpen: number;
  6 + shareSummary: string;
  7 + shareTitle: string;
  8 + shareUrl: string;
  9 +}
  1 +export interface VideoInfoDTO {
  2 + clarity: number;
  3 + resolutionHeight: number;
  4 + resolutionWidth: number;
  5 + videoDuration: number;
  6 + videoLandScape: number;
  7 + videoType: number;
  8 + videoUrl: string;
  9 +}
  1 +import { Logger, ResourcesUtils } from 'wdKit';
  2 +import { WDHttp } from 'wdNetwork'
  3 +import { ContentDetailDTO } from '../bean/ContentDetailDTO'
  4 +
  5 +const TAG = 'ContentDetailRequest';
  6 +
  7 +const mock_switch = true;
  8 +
  9 +export interface ContentDetailRequestParams {
  10 + contentId: string
  11 + relId: string
  12 + relType: string
  13 +}
  14 +
  15 +export class ContentDetailRequest {
  16 + static getContentDetailDataMock(context: Context): Promise<WDHttp.ResponseDTO<ContentDetailDTO[]>> {
  17 + Logger.info(TAG, `getContentDetailDataMock start`);
  18 + return ResourcesUtils.getResourcesJson<WDHttp.ResponseDTO<ContentDetailDTO[]>>(context, 'content_detail.json')
  19 + }
  20 +
  21 + /**
  22 + * 现网-新闻内容详情域名
  23 + */
  24 + static readonly HOST2: string = "https://pdapis.pdnews.cn";
  25 + /**
  26 + * 新闻内容详情【get】接口
  27 + */
  28 + static readonly CONTENT_DETAIL_PATH: string = "/api/rmrb-bff-display-zh/content/zh/c/content/detail";
  29 +
  30 + static getContentDetailUrl(contentId: string, relId: string, relType: string) {
  31 + let url = ContentDetailRequest.HOST2 + ContentDetailRequest.CONTENT_DETAIL_PATH
  32 + url = url + "?&contentId=" + contentId
  33 + + "&relId=" + relId
  34 + + "&relType=" + relType;
  35 + return url;
  36 + }
  37 +
  38 + static getContentDetail(params: ContentDetailRequestParams): Promise<WDHttp.ResponseDTO<ContentDetailDTO[]>> {
  39 + if (mock_switch) {
  40 + return ContentDetailRequest.getContentDetailDataMock(getContext());
  41 + }
  42 + let headers: Record<string, string> = {};
  43 + let url = ContentDetailRequest.getContentDetailUrl(params.contentId, params.relId, params.relType)
  44 + // let headers: HashMap<string, string> = HttpUrlUtils.getCommonHeaders();
  45 + return WDHttp.Request.get<WDHttp.ResponseDTO<ContentDetailDTO[]>>(url, headers)
  46 + }
  47 +}
  1 +import sensor from '@ohos.sensor';
  2 +import window from '@ohos.window';
  3 +import { WindowModel } from 'wdKit'
  4 +
  5 +export class devicePLSensorManager{
  6 + public static devicePLSensorOn(targetOrientation:number) {
  7 + try {
  8 + sensor.off(sensor.SensorId.ACCELEROMETER);
  9 + } catch (e) {}
  10 + let requestOrientation = -1; // sensor旋转的角度
  11 + let num = -1;
  12 + try{
  13 + // 订阅加速度传感器数据
  14 + sensor.on(sensor.SensorId.ACCELEROMETER, (response: sensor.AccelerometerResponse) => {
  15 + if(num < 5){
  16 + num ++;
  17 + return;
  18 + } else {
  19 + num = -1;
  20 + }
  21 + let orientation = -1;
  22 + let X = -response.x;
  23 + let Y = -response.y;
  24 + let Z = -response.z;
  25 + let magnitude = X * X + Y * Y;
  26 + if (magnitude * 4 >= Z * Z) {
  27 + let OneEightyOverPi = 57.29577957855;
  28 + let angle = Math.atan2(-Y, X) * OneEightyOverPi;
  29 + orientation = 90 - Math.round(angle);
  30 + while (orientation >= 360) {
  31 + orientation -= 360;
  32 + }
  33 + while (orientation < 0) {
  34 + orientation += 360;
  35 + }
  36 + }
  37 + if (orientation == -1) return; // 水平方向不处理
  38 + if (orientation > 315 || orientation < 45){
  39 + requestOrientation = window.Orientation.PORTRAIT;
  40 + }
  41 + else if (orientation > 45 && orientation < 135){
  42 + requestOrientation = window.Orientation.LANDSCAPE;
  43 + }
  44 + else if (orientation > 225 && orientation < 315){
  45 + requestOrientation = window.Orientation.LANDSCAPE_INVERTED;
  46 + }
  47 + if(targetOrientation == window.Orientation.PORTRAIT && requestOrientation == window.Orientation.PORTRAIT){
  48 + WindowModel.shared.setPreferredOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED);
  49 + sensor.off(sensor.SensorId.ACCELEROMETER);
  50 + }
  51 + if(targetOrientation == window.Orientation.LANDSCAPE && (requestOrientation == window.Orientation.LANDSCAPE || requestOrientation == window.Orientation.LANDSCAPE_INVERTED)){
  52 + WindowModel.shared.setPreferredOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED);
  53 + sensor.off(sensor.SensorId.ACCELEROMETER);
  54 + }
  55 + }, { interval: 2000000000 })
  56 + } catch (e) {
  57 + console.error(`屏幕状态报错:${e}`);
  58 + try {
  59 + sensor.off(sensor.SensorId.ACCELEROMETER);
  60 + } catch (e) {
  61 +
  62 + }
  63 + }
  64 + }
  65 +
  66 + public static devicePLSensorOff(){
  67 + try {
  68 + sensor.off(sensor.SensorId.ACCELEROMETER);
  69 + } catch (e) {}
  70 + }
  71 +}
  1 +import { SpeedBean, PlayerConstants } from 'wdPlayer';
  2 +
  3 +// 倍速Dialog
  4 +@Preview
  5 +@CustomDialog
  6 +export struct PlaySpeedDialog {
  7 + @Link playSpeed: number;
  8 + @Link @Watch('closeDialog') isFullScreen: boolean
  9 + controller: CustomDialogController;
  10 +
  11 + closeDialog(propName: string) {
  12 + if (this.isFullScreen == false) {
  13 + this.controller.close();
  14 + }
  15 + }
  16 +
  17 + build() {
  18 + Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
  19 + List({ space: '10%', initialIndex: 0 }) {
  20 + ForEach(PlayerConstants.SPEED_ARRAY, (item: SpeedBean) => {
  21 + ListItem() {
  22 + Text(item.text)
  23 + .width('100%')
  24 + .textAlign(TextAlign.Center)
  25 + .fontColor(this.playSpeed == item.value ? Color.Blue : Color.White)
  26 + .fontSize(16)
  27 + .fontWeight(FontWeight.Medium)
  28 + .onClick(() => {
  29 + this.playSpeed = item.value;
  30 + })
  31 + }
  32 + })
  33 + }
  34 + .padding(12)
  35 + .alignListItem(ListItemAlign.Center)
  36 + }
  37 + .width('20%')
  38 + .height('100%')
  39 + .backgroundColor('rgba(0,0,0,0.8)')
  40 + }
  41 +}
  42 +
  43 +// 码率..
  1 +import router from '@ohos.router';
  2 +import window from '@ohos.window';
  3 +import { WindowModel } from 'wdKit';
  4 +import { devicePLSensorManager } from '../utils/devicePortLandSensor';
  5 +
  6 +@Component
  7 +export struct PlayError {
  8 + @Consume message: string;
  9 + @Consume isFullScreen: boolean;
  10 +
  11 + build() {
  12 + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
  13 + Image($r('app.media.ic_back'))
  14 + .width($r("app.float.back_image_width"))
  15 + .height($r("app.float.back_image_width"))
  16 + .position({ x: $r("app.float.back_image_width"), y: $r("app.float.back_image_width") })
  17 + .aspectRatio(1)
  18 + .onClick(() => {
  19 + if (this.isFullScreen) {
  20 + this.isFullScreen = false;
  21 + WindowModel.shared.setPreferredOrientation(window.Orientation.PORTRAIT);
  22 + devicePLSensorManager.devicePLSensorOn(window.Orientation.PORTRAIT);
  23 + } else {
  24 + router.back();
  25 + }
  26 + })
  27 +
  28 + Text(this.message)
  29 + .width('100%')
  30 + .textAlign(TextAlign.Center)
  31 + .fontColor(Color.White)
  32 + .fontSize($r("app.float.font_size_14"))
  33 + .visibility(this.message ? Visibility.Visible : Visibility.None)
  34 + }
  35 + .width('100%')
  36 + .height('100%')
  37 + }
  38 +}
  1 +{
  2 + "module": {
  3 + "name": "wdDetailPlayApi",
  4 + "type": "shared",
  5 + "description": "$string:shared_desc",
  6 + "deviceTypes": [
  7 + "phone",
  8 + "tablet",
  9 + "2in1"
  10 + ],
  11 + "deliveryWithInstall": true,
  12 + "requestPermissions": [
  13 + {
  14 + "name":"ohos.permission.ACCELEROMETER"
  15 + }
  16 + ]
  17 + }
  18 +}
  1 +{
  2 + "color": [
  3 + {
  4 + "name": "white",
  5 + "value": "#FFFFFF"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "float": [
  3 + {
  4 + "name": "back_image_width",
  5 + "value": "16vp"
  6 + },
  7 + {
  8 + "name": "font_size_14",
  9 + "value": "14fp"
  10 + }
  11 + ]
  12 +}
  1 +{
  2 + "string": [
  3 + {
  4 + "name": "shared_desc",
  5 + "value": "description"
  6 + }
  7 + ]
  8 +}
  1 +import localUnitTest from './LocalUnit.test';
  2 +
  3 +export default function testsuite() {
  4 + localUnitTest();
  5 +}
  1 +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
  2 +
  3 +export default function localUnitTest() {
  4 + describe('localUnitTest',() => {
  5 + // Defines a test suite. Two parameters are supported: test suite name and test suite function.
  6 + beforeAll(() => {
  7 + // Presets an action, which is performed only once before all test cases of the test suite start.
  8 + // This API supports only one parameter: preset action function.
  9 + });
  10 + beforeEach(() => {
  11 + // Presets an action, which is performed before each unit test case starts.
  12 + // The number of execution times is the same as the number of test cases defined by **it**.
  13 + // This API supports only one parameter: preset action function.
  14 + });
  15 + afterEach(() => {
  16 + // Presets a clear action, which is performed after each unit test case ends.
  17 + // The number of execution times is the same as the number of test cases defined by **it**.
  18 + // This API supports only one parameter: clear action function.
  19 + });
  20 + afterAll(() => {
  21 + // Presets a clear action, which is performed after all test cases of the test suite end.
  22 + // This API supports only one parameter: clear action function.
  23 + });
  24 + it('assertContain', 0, () => {
  25 + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
  26 + let a = 'abc';
  27 + let b = 'b';
  28 + // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
  29 + expect(a).assertContain(b);
  30 + expect(a).assertEqual(a);
  31 + });
  32 + });
  33 +}
  1 +/node_modules
  2 +/oh_modules
  3 +/.preview
  4 +/build
  5 +/.cxx
  6 +/.test
  1 +export { DetailPlayLivePage } from './src/main/ets/pages/DetailPlayLivePage'
  1 +{
  2 + "apiType": "stageMode",
  3 + "buildOption": {
  4 + "arkOptions": {
  5 + // "apPath": "./modules.ap" /* Profile used for profile-guided optimization (PGO), a compiler optimization technique to improve app runtime performance. */
  6 + }
  7 + },
  8 + "buildOptionSet": [
  9 + {
  10 + "name": "release",
  11 + "arkOptions": {
  12 + "obfuscation": {
  13 + "ruleOptions": {
  14 + "enable": true,
  15 + "files": [
  16 + "./obfuscation-rules.txt"
  17 + ]
  18 + }
  19 + }
  20 + }
  21 + },
  22 + ],
  23 + "targets": [
  24 + {
  25 + "name": "default"
  26 + }
  27 + ]
  28 +}
  1 +import { hspTasks } from '@ohos/hvigor-ohos-plugin';
  2 +
  3 +export default {
  4 + system: hspTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
  5 + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
  6 +}
  1 +# Define project specific obfuscation rules here.
  2 +# You can include the obfuscation configuration files in the current module's build-profile.json5.
  3 +#
  4 +# For more details, see
  5 +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
  6 +
  7 +# Obfuscation options:
  8 +# -disable-obfuscation: disable all obfuscations
  9 +# -enable-property-obfuscation: obfuscate the property names
  10 +# -enable-toplevel-obfuscation: obfuscate the names in the global scope
  11 +# -compact: remove unnecessary blank spaces and all line feeds
  12 +# -remove-log: remove all console.* statements
  13 +# -print-namecache: print the name cache that contains the mapping from the old names to new names
  14 +# -apply-namecache: reuse the given cache file
  15 +
  16 +# Keep options:
  17 +# -keep-property-name: specifies property names that you want to keep
  18 +# -keep-global-name: specifies names that you want to keep in the global scope
  1 +{
  2 + "name": "wddetailplaylive",
  3 + "version": "1.0.0",
  4 + "description": "Please describe the basic information.",
  5 + "main": "Index.ets",
  6 + "author": "",
  7 + "license": "Apache-2.0",
  8 + "dependencies": {
  9 + }
  10 +}
  1 +@Entry
  2 +@Component
  3 +export struct DetailPlayLivePage {
  4 + @State message: string = 'Detail Play Live Page';
  5 +
  6 + build() {
  7 + Row() {
  8 + Column() {
  9 + Text(this.message)
  10 + .fontSize(50)
  11 + .fontWeight(FontWeight.Bold)
  12 + }
  13 + .width('100%')
  14 + }
  15 + .height('100%')
  16 + }
  17 +}
  1 +{
  2 + "module": {
  3 + "name": "wdDetailPlayLive",
  4 + "type": "shared",
  5 + "description": "$string:shared_desc",
  6 + "deviceTypes": [
  7 + "phone",
  8 + "tablet",
  9 + "2in1"
  10 + ],
  11 + "deliveryWithInstall": true,
  12 + "pages": "$profile:main_pages"
  13 + }
  14 +}
  1 +{
  2 + "color": [
  3 + {
  4 + "name": "white",
  5 + "value": "#FFFFFF"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "float": [
  3 + {
  4 + "name": "image_width",
  5 + "value": "24vp"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "string": [
  3 + {
  4 + "name": "shared_desc",
  5 + "value": "description"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "src": [
  3 + "pages/DetailPlayLivePage"
  4 + ]
  5 +}
  1 +import localUnitTest from './LocalUnit.test';
  2 +
  3 +export default function testsuite() {
  4 + localUnitTest();
  5 +}
  1 +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
  2 +
  3 +export default function localUnitTest() {
  4 + describe('localUnitTest',() => {
  5 + // Defines a test suite. Two parameters are supported: test suite name and test suite function.
  6 + beforeAll(() => {
  7 + // Presets an action, which is performed only once before all test cases of the test suite start.
  8 + // This API supports only one parameter: preset action function.
  9 + });
  10 + beforeEach(() => {
  11 + // Presets an action, which is performed before each unit test case starts.
  12 + // The number of execution times is the same as the number of test cases defined by **it**.
  13 + // This API supports only one parameter: preset action function.
  14 + });
  15 + afterEach(() => {
  16 + // Presets a clear action, which is performed after each unit test case ends.
  17 + // The number of execution times is the same as the number of test cases defined by **it**.
  18 + // This API supports only one parameter: clear action function.
  19 + });
  20 + afterAll(() => {
  21 + // Presets a clear action, which is performed after all test cases of the test suite end.
  22 + // This API supports only one parameter: clear action function.
  23 + });
  24 + it('assertContain', 0, () => {
  25 + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
  26 + let a = 'abc';
  27 + let b = 'b';
  28 + // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
  29 + expect(a).assertContain(b);
  30 + expect(a).assertEqual(a);
  31 + });
  32 + });
  33 +}
  1 +/node_modules
  2 +/oh_modules
  3 +/.preview
  4 +/build
  5 +/.cxx
  6 +/.test
  1 +export { DetailPlayShortVideoPage } from './src/main/ets/pages/DetailPlayShortVideoPage'
  1 +{
  2 + "apiType": "stageMode",
  3 + "buildOption": {
  4 + "arkOptions": {
  5 + // "apPath": "./modules.ap" /* Profile used for profile-guided optimization (PGO), a compiler optimization technique to improve app runtime performance. */
  6 + }
  7 + },
  8 + "buildOptionSet": [
  9 + {
  10 + "name": "release",
  11 + "arkOptions": {
  12 + "obfuscation": {
  13 + "ruleOptions": {
  14 + "enable": true,
  15 + "files": [
  16 + "./obfuscation-rules.txt"
  17 + ]
  18 + }
  19 + }
  20 + }
  21 + },
  22 + ],
  23 + "targets": [
  24 + {
  25 + "name": "default"
  26 + }
  27 + ]
  28 +}
  1 +import { hspTasks } from '@ohos/hvigor-ohos-plugin';
  2 +
  3 +export default {
  4 + system: hspTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
  5 + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
  6 +}
  1 +# Define project specific obfuscation rules here.
  2 +# You can include the obfuscation configuration files in the current module's build-profile.json5.
  3 +#
  4 +# For more details, see
  5 +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
  6 +
  7 +# Obfuscation options:
  8 +# -disable-obfuscation: disable all obfuscations
  9 +# -enable-property-obfuscation: obfuscate the property names
  10 +# -enable-toplevel-obfuscation: obfuscate the names in the global scope
  11 +# -compact: remove unnecessary blank spaces and all line feeds
  12 +# -remove-log: remove all console.* statements
  13 +# -print-namecache: print the name cache that contains the mapping from the old names to new names
  14 +# -apply-namecache: reuse the given cache file
  15 +
  16 +# Keep options:
  17 +# -keep-property-name: specifies property names that you want to keep
  18 +# -keep-global-name: specifies names that you want to keep in the global scope
  1 +{
  2 + "name": "wddetailplayshortvideo",
  3 + "version": "1.0.0",
  4 + "description": "Please describe the basic information.",
  5 + "main": "Index.ets",
  6 + "author": "",
  7 + "license": "Apache-2.0",
  8 + "dependencies": {
  9 + "wdBean": "file:../wdBean",
  10 + "wdPlayer": "file:../wdPlayer",
  11 + "wdKit": "file:../wdKit",
  12 + "wdNetwork": "file:../wdNetwork",
  13 + "wdDetailPlayApi": "file:../wdDetailPlayApi",
  14 + "wdRouter": "file:../wdRouter"
  15 + }
  16 +}
  1 +import router from '@ohos.router';
  2 +import mediaquery from '@ohos.mediaquery';
  3 +import window from '@ohos.window';
  4 +import { Action } from 'wdBean';
  5 +import { WindowModel, SPHelper, Logger } from 'wdKit';
  6 +import { WDPlayerController, WDPlayerRenderView, PlayerConstants } from 'wdPlayer';
  7 +import { devicePLSensorManager } from 'wdDetailPlayApi';
  8 +import { PlayControlViewContainer } from '../view/PlayControlViewContainer';
  9 +import { PlayerDetailContainer } from '../view/PlayerDetailContainer';
  10 +import { PlayViewModel } from '../viewmodel/PlayViewModel';
  11 +import { DetailContainer } from '../view/DetailContainer';
  12 +
  13 +const TAG = 'DetailPlayShortVideoPage';
  14 +
  15 +/**
  16 + * 详情&短视频播放页面
  17 + */
  18 +@Entry
  19 +@Component
  20 +export struct DetailPlayShortVideoPage {
  21 + private contentId?: string = undefined
  22 + private playerController: WDPlayerController = new WDPlayerController();
  23 + @Watch("urlChanged") @State url?: string = undefined
  24 + @Watch('changeContinue') @Provide nextContId?: string = '';
  25 + @Watch('getPlayHistory') @Provide curContId?: string = undefined;
  26 + @Watch("playVMChanged") @Provide playVM: PlayViewModel = new PlayViewModel();
  27 + @Provide isFullScreen: boolean = false;
  28 + @Provide canStart?: boolean = false;
  29 + @Provide status: number = PlayerConstants.STATUS_START;
  30 + @Provide userId: string = '';
  31 + @Provide title?: string = undefined
  32 + @Provide message?: string = undefined
  33 +
  34 + playVMChanged(name: string) {
  35 + this.url = this.playVM.url
  36 + this.title = this.playVM.title
  37 + this.curContId = this.playVM.contentId
  38 + this.nextContId = this.playVM.nextContId
  39 + this.canStart = this.playVM.canStart;
  40 + this.message = this.playVM.message
  41 + }
  42 +
  43 + aboutToAppear() {
  44 + let action: Action = router.getParams() as Action
  45 + if (action) {
  46 + this.contentId = action.params?.contentID
  47 + }
  48 + // 设置播放地址
  49 + // this.url = 'https://media.w3.org/2010/05/sintel/trailer.mp4'
  50 + let listener = mediaquery.matchMediaSync('(orientation: landscape)');
  51 + listener.on("change", (mediaQueryResult) => {
  52 + if (mediaQueryResult.matches) {
  53 + console.log("横屏 yes")
  54 + this.isFullScreen = true
  55 + } else {
  56 + this.isFullScreen = false
  57 + console.log("横屏 no")
  58 + }
  59 + WindowModel.shared.setMainWindowFullScreen(this.isFullScreen)
  60 + })
  61 + }
  62 +
  63 + onPageShow() {
  64 + WindowModel.shared.setPreferredOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED);
  65 + }
  66 +
  67 + onPageHide() {
  68 + WindowModel.shared.setPreferredOrientation(window.Orientation.PORTRAIT);
  69 + devicePLSensorManager.devicePLSensorOff();
  70 + this.status = PlayerConstants.STATUS_PAUSE;
  71 + this.playerController?.pause();
  72 + }
  73 +
  74 + @Builder
  75 + playerViewContainer() {
  76 + // 播放窗口
  77 + WDPlayerRenderView({
  78 + playerController: this.playerController,
  79 + onLoad: async () => {
  80 + this.playVM.playWithContentId(this.contentId ?? "846899373")
  81 + }
  82 + })
  83 + .height('100%')
  84 + .width('100%')
  85 + }
  86 +
  87 + @Builder
  88 + playControlViewContainer() {
  89 + // 播放窗口控制bar
  90 + PlayControlViewContainer({
  91 + playerController: this.playerController
  92 + })
  93 + }
  94 +
  95 + @Builder
  96 + detailContainer() {
  97 + // DetailTabBarPageComponent({ pageId: this.pageId }).backgroundColor(Color.Black)
  98 + DetailContainer()
  99 + }
  100 +
  101 + build() {
  102 + Row() {
  103 + PlayerDetailContainer({ playerView: () => {
  104 + this.playerViewContainer()
  105 + }, playControlView: () => {
  106 + this.playControlViewContainer()
  107 + }, detailView: () => {
  108 + this.detailContainer()
  109 + } })
  110 + .height('100%')
  111 + .width('100%')
  112 + }
  113 + .height('100%')
  114 + .width('100%')
  115 + .backgroundColor(Color.Black)
  116 + }
  117 +
  118 + // 续播判断
  119 + changeContinue() {
  120 + if (this.nextContId) {
  121 + this.playerController.continue = () => {
  122 + this.playerController?.stop();
  123 + this.playVM.playWithContentId(this.nextContId ?? '');
  124 + }
  125 + return;
  126 + }
  127 + this.playerController.continue = undefined;
  128 + }
  129 +
  130 + urlChanged(name: string) {
  131 + if (this.url) {
  132 + console.log("url:" + this.url);
  133 + this.status = PlayerConstants.STATUS_START;
  134 + this.playerController.firstPlay(this.url);
  135 + }
  136 + }
  137 +
  138 + getPlayHistory() {
  139 + SPHelper.default.get('playHistory', '').then((str) => {
  140 + let result = str.toString();
  141 + let time = 0;
  142 + if (result != null && result != "") {
  143 + let playHistory: Record<string, Record<string, number>> = JSON.parse(result);
  144 + let userData: Record<string, number> = {};
  145 + if (this.userId) {
  146 + userData = playHistory[this.userId] ?? {};
  147 + }
  148 + if (this.curContId) {
  149 + time = userData?.[this.curContId] ?? 0;
  150 + }
  151 + }
  152 + this.playerController?.setStartTime(time);
  153 + }).catch((err: Error) => {
  154 + // Error: Inner error. Error code 15500000
  155 + Logger.error(TAG, 'catch err:' + JSON.stringify(err));
  156 + this.playerController?.setStartTime(0);
  157 + });
  158 + }
  159 +}
  1 +import { PlayerTitleComment } from './PlayerTitleComment'
  2 +
  3 +/**
  4 + * 非全屏状态-(播放页面底部)非播放区域
  5 + */
  6 +@Component
  7 +export struct DetailContainer {
  8 + build() {
  9 + PlayerTitleComment()
  10 + .width('100%')
  11 + }
  12 +}
  1 +import window from '@ohos.window';
  2 +import { WindowModel, SPHelper } from 'wdKit';
  3 +import { DateFormatUtil, WDPlayerController, PlayerConstants } from 'wdPlayer';
  4 +import { devicePLSensorManager, PlayError } from 'wdDetailPlayApi';
  5 +import { PlayerProgressBar } from './PlayerProgressBar';
  6 +import { PlayerTitle } from './PlayerTitle';
  7 +
  8 +/**
  9 + * 播放窗口上层的控制view
  10 + */
  11 +@Component
  12 +export struct PlayControlViewContainer {
  13 + playerController?: WDPlayerController;
  14 + @Consume status: number;
  15 + @Provide currentTime: string = "00:00";
  16 + @Provide totalTime: string = "00:00";
  17 + @Provide progressVal: number = 0;
  18 + @Provide isShowVolume: boolean = false;
  19 + @Provide volumeProgress: number = 1;
  20 + @Consume isFullScreen: boolean;
  21 + @State isLocked: boolean = false;
  22 + @Provide setAuto: number | undefined = undefined;
  23 + @Consume canStart: boolean;
  24 + @Consume userId: string;
  25 + @Consume curContId: string;
  26 + @Consume message: string;
  27 + // 用于触发拖动手势事件,滑动的最小距离为5vp时拖动手势识别成功。
  28 + private panOptionBright: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Vertical });
  29 + private panOptionVolume: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Vertical });
  30 +
  31 + aboutToAppear() {
  32 + if (this.playerController == null) {
  33 + return
  34 + }
  35 + this.playerController.onTimeUpdate = (position, duration) => {
  36 + this.currentTime = DateFormatUtil.secondToTime(position);
  37 + this.totalTime = DateFormatUtil.secondToTime(duration);
  38 + this.progressVal = Math.floor(position * 100 / duration);
  39 + this.setPlayHistory(position);
  40 + }
  41 +
  42 + this.playerController.onVolumeUpdate = (volume) => {
  43 + this.volumeProgress = volume;
  44 + }
  45 + }
  46 +
  47 + aboutToDisappear() {
  48 + this.playerController?.release();
  49 + }
  50 +
  51 + build() {
  52 + Stack({ alignContent: Alignment.Start }) {
  53 + Stack() {
  54 + Column() {
  55 + PlayerTitle({ playerController: this.playerController })
  56 + .width('100%')
  57 + .height(44)
  58 + .visibility(this.isFullScreen ? Visibility.Visible : Visibility.None)
  59 +
  60 + Row() {
  61 + Column()
  62 + .width('50%')
  63 + .height('100%')
  64 + .gesture(
  65 + PanGesture(this.panOptionBright)
  66 + .onActionStart((event?: GestureEvent) => {
  67 + this.playerController?.onBrightActionStart(event!);
  68 + })
  69 + .onActionUpdate((event?: GestureEvent) => {
  70 + this.playerController?.onBrightActionUpdate(event!);
  71 + })
  72 + .onActionEnd(() => {
  73 + this.playerController?.onActionEnd();
  74 + })
  75 + )
  76 + Column()
  77 + .width('50%')
  78 + .height('100%')
  79 + .gesture(
  80 + PanGesture(this.panOptionVolume)
  81 + .onActionStart((event?: GestureEvent) => {
  82 + this.isShowVolume = true
  83 + this.playerController?.onVolumeActionStart(event!);
  84 + })
  85 + .onActionUpdate((event?: GestureEvent) => {
  86 + this.playerController?.onVolumeActionUpdate(event!);
  87 + })
  88 + .onActionEnd(() => {
  89 + setTimeout(() => {
  90 + this.isShowVolume = false
  91 + }, 500)
  92 + this.playerController?.onActionEnd();
  93 + })
  94 + )
  95 + }
  96 + .width('100%')
  97 + .layoutWeight(1)
  98 +
  99 + PlayerProgressBar({ playerController: this.playerController })
  100 + .width('100%')
  101 + .height(this.isFullScreen ? 66 : 44)
  102 + }
  103 + .height('100%')
  104 + .width('100%')
  105 + .zIndex(1)
  106 + .gesture(TapGesture({ count: 2 })
  107 + .onAction((event: GestureEvent) => {
  108 + let curStatus = (this.status === PlayerConstants.STATUS_START);
  109 + this.status = curStatus ? PlayerConstants.STATUS_PAUSE : PlayerConstants.STATUS_START;
  110 + this.playerController?.switchPlayOrPause();
  111 + }))
  112 +
  113 + Row() {
  114 + Image($r('app.media.ic_volume'))
  115 + .width(20)
  116 + .height(20)
  117 + Progress({ value: this.volumeProgress * 100, total: 100, type: ProgressType.Linear })
  118 + .width(100)
  119 + .height(5)
  120 + .zIndex(2)
  121 + }
  122 + .width(140)
  123 + .height(40)
  124 + .borderRadius(4)
  125 + .backgroundColor("#FFFFFF")
  126 + .opacity(0.5)
  127 + .justifyContent(FlexAlign.Center)
  128 + .visibility(this.isShowVolume ? Visibility.Visible : Visibility.None)
  129 +
  130 + }.visibility(!this.canStart || this.isLocked ? Visibility.None : Visibility.Visible)
  131 +
  132 + Image(this.isLocked ? $r('app.media.ic_lock') : $r('app.media.ic_unlock'))
  133 + .width(20)
  134 + .height(20)
  135 + .margin({ left: 20 })
  136 + .borderRadius(4)
  137 + .zIndex(2)
  138 + .visibility(this.canStart && this.isFullScreen ? Visibility.Visible : Visibility.None)
  139 + .gesture(TapGesture()
  140 + .onAction((event: GestureEvent) => {
  141 + this.isLocked = !this.isLocked;
  142 + if (this.isLocked) {
  143 + WindowModel.shared.setPreferredOrientation(window.Orientation.LOCKED);
  144 + devicePLSensorManager.devicePLSensorOff();
  145 + } else {
  146 + WindowModel.shared.setPreferredOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED);
  147 + }
  148 + }))
  149 +
  150 + PlayError()
  151 + .width('100%')
  152 + .height('100%')
  153 + .zIndex(3)// zIndex值越大,显示层级越高,即zIndex值大的组件会覆盖在zIndex值小的组件上方。
  154 + .visibility(this.canStart == undefined && this.message != undefined ? Visibility.Visible : Visibility.None)
  155 + }
  156 + }
  157 +
  158 + setPlayHistory(position: number) {
  159 + if (this.userId && this.curContId) {
  160 + SPHelper.default.get('playHistory', '').then((str) => {
  161 + let result = str.toString();
  162 + let playHistory: Record<string, Record<string, number>> = {};
  163 + if (result != null && result != "") {
  164 + playHistory = JSON.parse(result);
  165 + }
  166 + let userData = playHistory[this.userId] ?? {};
  167 + userData[this.curContId] = position;
  168 + playHistory[this.userId] = userData;
  169 + SPHelper.default.save('playHistory', JSON.stringify(playHistory));
  170 + })
  171 + }
  172 + }
  173 +}
  1 +/**
  2 + * 详情view&播放器view的容器布局组件
  3 + */
  4 +@Component
  5 +export struct PlayerDetailContainer {
  6 + @BuilderParam playerView: () => void
  7 + @BuilderParam playControlView: () => void
  8 + @BuilderParam detailView: () => void
  9 + @Consume isFullScreen: boolean
  10 +
  11 + build() {
  12 + RelativeContainer() {
  13 + Stack() {
  14 + Row() {
  15 + this.playerView()
  16 + }
  17 + .height('100%')
  18 + .width('100%')
  19 + .zIndex(0)
  20 +
  21 + Row() {
  22 + this.playControlView()
  23 + }
  24 + .height('100%')
  25 + .width('100%')
  26 + .zIndex(1)
  27 + }
  28 + .width('100%')
  29 + .aspectRatio(this.isFullScreen ? 0 : 16 / 9.0)
  30 + .alignRules({
  31 + center: { anchor: '__container__', align: VerticalAlign.Center },
  32 + middle: { anchor: '__container__', align: HorizontalAlign.Center }
  33 + })
  34 + .id('txt_title')
  35 +
  36 + if (!this.isFullScreen) {
  37 + Row() {
  38 + this.detailView()
  39 + }
  40 + .width('100%')
  41 + .alignRules({
  42 + bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
  43 + })
  44 + .id('row_bottomView')
  45 + }
  46 + }
  47 + .width('100%')
  48 + .height('100%')
  49 + }
  50 +}
  1 +import window from '@ohos.window';
  2 +import { WindowModel } from 'wdKit';
  3 +import { WDPlayerController, PlayerConstants } from 'wdPlayer';
  4 +import { devicePLSensorManager } from 'wdDetailPlayApi';
  5 +import { PlaySpeedDialog } from 'wdDetailPlayApi';
  6 +import { PlayViewModel } from '../viewmodel/PlayViewModel';
  7 +
  8 +/**
  9 + * 全屏播放器进度条
  10 + */
  11 +@Component
  12 +export struct PlayerProgressBar {
  13 + private playerController?: WDPlayerController;
  14 + @State curContId?: string = undefined;
  15 + @State @Watch('switchSpeed') playSpeed: number = 1;
  16 + @Consume currentTime: string;
  17 + @Consume totalTime: string;
  18 + @Consume progressVal: number;
  19 + @Consume status: number;
  20 + @Consume isFullScreen: boolean;
  21 + @Consume nextContId: string;
  22 + @Consume @Watch("playVMChanged") playVM: PlayViewModel;
  23 + speedDialogController: CustomDialogController = new CustomDialogController({
  24 + builder: PlaySpeedDialog({
  25 + playSpeed: $playSpeed,
  26 + isFullScreen: $isFullScreen
  27 + }),
  28 + alignment: DialogAlignment.CenterEnd,
  29 + customStyle: true
  30 + })
  31 +
  32 + switchSpeed() {
  33 + this.playerController?.setSpeed(this.playSpeed);
  34 + }
  35 +
  36 + switchPause() {
  37 + this.status = PlayerConstants.STATUS_PAUSE;
  38 + this.playerController?.pause();
  39 + }
  40 +
  41 + switchSubId(contId?: string) {
  42 + if (contId) {
  43 + this.playerController?.stop();
  44 + this.playVM.playWithContentId(contId);
  45 + }
  46 + }
  47 +
  48 + playVMChanged(name: string) {
  49 + this.curContId = this.playVM.contentId
  50 + }
  51 +
  52 + build() {
  53 + Column() {
  54 + Row() {
  55 + Column() {
  56 + Image(this.status === PlayerConstants.STATUS_START ?
  57 + $r('app.media.ic_pause') : $r('app.media.ic_play'))
  58 + .width($r('app.float.control_image_width'))
  59 + .aspectRatio(1)
  60 + .onClick(() => {
  61 + let curStatus = (this.status === PlayerConstants.STATUS_START);
  62 + this.status = curStatus ? PlayerConstants.STATUS_PAUSE : PlayerConstants.STATUS_START;
  63 + this.playerController?.switchPlayOrPause();
  64 + })
  65 + }
  66 + .justifyContent(FlexAlign.Center)
  67 + .alignItems(HorizontalAlign.Center)
  68 + .width(44)
  69 + .height('100%')
  70 +
  71 + Text(this.currentTime)
  72 + .fontSize($r("app.float.font_size_14"))
  73 + .fontColor(Color.White)
  74 + Slider({
  75 + value: this.progressVal,
  76 + step: 1,
  77 + style: SliderStyle.OutSet
  78 + })
  79 + .blockColor(Color.White)
  80 + .trackColor($r('app.color.track_color'))
  81 + .selectedColor($r('app.color.index_tab_selected_font_color'))
  82 + .trackThickness(1)
  83 + .layoutWeight(1)
  84 + .onChange((value: number, mode: SliderChangeMode) => {
  85 + this.playerController?.setSeekTime(value, mode);
  86 + })
  87 + Text(this.totalTime)
  88 + .fontSize($r("app.float.font_size_14"))
  89 + .fontColor(Color.White)
  90 +
  91 + Column() {
  92 + Image($r('app.media.ic_fullscreen'))
  93 + .width($r('app.float.control_image_width'))
  94 + .aspectRatio(1)
  95 + .onClick(() => {
  96 + this.isFullScreen = !this.isFullScreen;
  97 + WindowModel.shared.setPreferredOrientation(window.Orientation.LANDSCAPE);
  98 + devicePLSensorManager.devicePLSensorOn(window.Orientation.LANDSCAPE);
  99 + })
  100 + }
  101 + .justifyContent(FlexAlign.Center)
  102 + .alignItems(HorizontalAlign.Center)
  103 + .width(44)
  104 + .height('100%')
  105 + }
  106 + .width('100%')
  107 + .visibility(this.isFullScreen ? Visibility.None : Visibility.Visible)
  108 +
  109 + Column() {
  110 + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) {
  111 + Text(this.currentTime)
  112 + .fontSize($r("app.float.font_size_14"))
  113 + .fontColor(Color.White)
  114 +
  115 + Slider({
  116 + value: this.progressVal,
  117 + step: 1
  118 + })
  119 + .blockColor(Color.White)
  120 + .trackColor($r('app.color.track_color'))
  121 + .selectedColor($r('app.color.index_tab_selected_font_color'))
  122 + .trackThickness(1)
  123 + .layoutWeight(1)
  124 + .onChange((value: number, mode: SliderChangeMode) => {
  125 + this.playerController?.setSeekTime(value, mode);
  126 + })
  127 +
  128 + Text(this.totalTime)
  129 + .fontSize($r("app.float.font_size_14"))
  130 + .fontColor(Color.White)
  131 + }
  132 + .width('98%')
  133 + .height('40%')
  134 + .padding({ left: '1%', right: '1%' })
  135 +
  136 + Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Start }) {
  137 + Row() {
  138 + Image(this.status === PlayerConstants.STATUS_START ?
  139 + $r('app.media.ic_pause') : $r('app.media.ic_play'))
  140 + .width($r('app.float.control_image_width'))
  141 + .aspectRatio(1)
  142 + .onClick(() => {
  143 + let curStatus = (this.status === PlayerConstants.STATUS_START);
  144 + this.status = curStatus ? PlayerConstants.STATUS_PAUSE : PlayerConstants.STATUS_START;
  145 + this.playerController?.switchPlayOrPause();
  146 + })
  147 + .margin({ right: '4%' })
  148 +
  149 + Image($r('app.media.ic_next'))
  150 + .width($r('app.float.control_image_width'))
  151 + .aspectRatio(1)
  152 + .margin({ right: '4%' })
  153 + .visibility(this.nextContId ? Visibility.Visible : Visibility.None)
  154 + .onClick(() => {
  155 + this.playerController?.stop();
  156 + this.playVM.playWithContentId(this.nextContId);
  157 + })
  158 +
  159 + }
  160 + .justifyContent(FlexAlign.Start)
  161 + .width('50%')
  162 + .height('100%')
  163 + .padding({ left: '2%' })
  164 +
  165 + Row() {
  166 + Text(this.playSpeed == 1 ? '倍速' : PlayerConstants.SPEED_ARRAY[this.playSpeed].text)
  167 + .fontSize($r("app.float.font_size_14"))
  168 + .fontColor(Color.White)
  169 + .margin({ right: '4%' })
  170 + .onClick(() => {
  171 + this.speedDialogController.open();
  172 + })
  173 +
  174 + Column() {
  175 + Image($r('app.media.ic_fullscreen'))
  176 + .width($r('app.float.control_image_width'))
  177 + .aspectRatio(1)
  178 + .onClick(() => {
  179 + this.isFullScreen = !this.isFullScreen;
  180 + WindowModel.shared.setPreferredOrientation(window.Orientation.PORTRAIT);
  181 + devicePLSensorManager.devicePLSensorOn(window.Orientation.PORTRAIT);
  182 + })
  183 + }
  184 + .justifyContent(FlexAlign.Center)
  185 + .alignItems(HorizontalAlign.Center)
  186 + .width(44)
  187 + .height('100%')
  188 + .margin({ right: '2%' })
  189 + }
  190 + .justifyContent(FlexAlign.End)
  191 + .width('50%')
  192 + .height('100%')
  193 + .margin({ right: '2%' })
  194 + }
  195 + .width('100%')
  196 + .height('60%')
  197 + }
  198 + .width('100%')
  199 + .height('100%')
  200 + .visibility(this.isFullScreen ? Visibility.Visible : Visibility.None)
  201 +
  202 + }
  203 + .width('100%')
  204 + .height('100%')
  205 + .justifyContent(FlexAlign.Center)
  206 + }
  207 +}
  1 +import router from '@ohos.router';
  2 +import window from '@ohos.window';
  3 +import deviceInfo from '@ohos.deviceInfo'
  4 +import { WindowModel } from 'wdKit';
  5 +import { WDPlayerController } from 'wdPlayer';
  6 +import { devicePLSensorManager } from 'wdDetailPlayApi';
  7 +
  8 +@Component
  9 +export struct PlayerTitle {
  10 + private playerController?: WDPlayerController;
  11 + @Consume title?: string
  12 + @Consume isFullScreen: boolean;
  13 + @State @Watch('watchSpeed') playSpeed: number = 1;
  14 +
  15 + aboutToAppear() {
  16 + }
  17 +
  18 + watchSpeed() {
  19 + this.playerController?.setSpeed(this.playSpeed);
  20 + }
  21 +
  22 + build() {
  23 + Row() {
  24 + Image($r('app.media.ic_back'))
  25 + .width(44)
  26 + .padding(13)
  27 + .aspectRatio(1)
  28 + .onClick(() => {
  29 + if (this.isFullScreen) {
  30 + if (deviceInfo.deviceType != "phone") {
  31 + WindowModel.shared.getWindowSize().then((size) => {
  32 + if (size.width > size.height) {
  33 + router.back();
  34 + } else {
  35 + this.isFullScreen = !this.isFullScreen;
  36 + WindowModel.shared.setPreferredOrientation(window.Orientation.PORTRAIT);
  37 + devicePLSensorManager.devicePLSensorOn(window.Orientation.PORTRAIT);
  38 + }
  39 + })
  40 + } else {
  41 + this.isFullScreen = !this.isFullScreen;
  42 + WindowModel.shared.setPreferredOrientation(window.Orientation.PORTRAIT);
  43 + devicePLSensorManager.devicePLSensorOn(window.Orientation.PORTRAIT);
  44 + }
  45 + } else {
  46 + router.back();
  47 + }
  48 + })
  49 + Text(this.title)
  50 + .fontColor(Color.White)
  51 + .fontSize('14fp')
  52 + .maxLines(2)
  53 + .layoutWeight(1)
  54 + }.alignItems(VerticalAlign.Center)
  55 + }
  56 +}
  1 +import router from '@ohos.router';
  2 +import window from '@ohos.window';
  3 +import deviceInfo from '@ohos.deviceInfo'
  4 +import { WindowModel } from 'wdKit';
  5 +import { WDPlayerController } from 'wdPlayer';
  6 +import { devicePLSensorManager } from 'wdDetailPlayApi';
  7 +
  8 +@Component
  9 +export struct PlayerTitleComment {
  10 + private playerController?: WDPlayerController;
  11 + @Consume title?: string
  12 + @State @Watch('watchSpeed') playSpeed: number = 1;
  13 + @Consume isFullScreen: boolean;
  14 + @State comment: string = '';
  15 +
  16 + aboutToAppear() {
  17 + }
  18 +
  19 + watchSpeed() {
  20 + this.playerController?.setSpeed(this.playSpeed);
  21 + }
  22 +
  23 + build() {
  24 + Column() {
  25 + Text(this.title)
  26 + .fontColor(Color.White)
  27 + .fontSize(14)
  28 + .maxLines(1)
  29 +
  30 + Text('查看详情 > ')
  31 + .fontColor(Color.White)
  32 + .fontSize('14fp')
  33 + .maxLines(2)
  34 +
  35 + Divider().height(30)
  36 +
  37 + Row() {
  38 + Image($r('app.media.ic_back'))
  39 + .width(44)
  40 + .aspectRatio(1)
  41 + .padding(13)
  42 + .margin({ left: 13 })
  43 + .onClick(() => {
  44 + if (this.isFullScreen) {
  45 + if (deviceInfo.deviceType != "phone") {
  46 + WindowModel.shared.getWindowSize().then((size) => {
  47 + if (size.width > size.height) {
  48 + router.back();
  49 + } else {
  50 + this.isFullScreen = !this.isFullScreen;
  51 + WindowModel.shared.setPreferredOrientation(window.Orientation.PORTRAIT);
  52 + devicePLSensorManager.devicePLSensorOn(window.Orientation.PORTRAIT);
  53 + }
  54 + })
  55 + } else {
  56 + this.isFullScreen = !this.isFullScreen;
  57 + WindowModel.shared.setPreferredOrientation(window.Orientation.PORTRAIT);
  58 + devicePLSensorManager.devicePLSensorOn(window.Orientation.PORTRAIT);
  59 + }
  60 + } else {
  61 + router.back();
  62 + }
  63 + })
  64 +
  65 + TextInput({ placeholder: '说两句...', text: this.comment })
  66 + .placeholderColor(Color.White)
  67 + .placeholderFont({ size: 14 })
  68 + .fontColor(Color.White)
  69 + .fontSize(14)
  70 + .maxLines(1)
  71 + .layoutWeight(1)
  72 + }.alignItems(VerticalAlign.Center)
  73 + }
  74 + .width('100%')
  75 + .alignItems(HorizontalAlign.Start)
  76 + }
  77 +}
  1 +import { BusinessError } from '@ohos.base'
  2 +import { Logger } from 'wdKit'
  3 +import { WDHttp } from 'wdNetwork'
  4 +import { ContentDetailRequest } from 'wdDetailPlayApi'
  5 +import { ContentDetailDTO } from 'wdDetailPlayApi/src/main/ets/bean/ContentDetailDTO'
  6 +
  7 +const TAG = 'PlayViewModel';
  8 +
  9 +@Observed
  10 +export class PlayViewModel {
  11 + contentId: string
  12 + relId: string
  13 + relType: string
  14 + title?: string
  15 + url?: string
  16 + nextContId?: string
  17 + canStart?: boolean
  18 + message?: string
  19 +
  20 + constructor() {
  21 + // todo:
  22 + this.contentId = '30013266075'
  23 + this.relId = '500000301942'
  24 + this.relType = '1'
  25 +
  26 + // this.getGlobalInfo();
  27 + }
  28 +
  29 + playWithContentId(contentId: string) {
  30 + this.contentId = contentId;
  31 + this.getContentDetailData() // 包括播放地址PlayUrl
  32 + }
  33 +
  34 + getContentDetailData() {
  35 + ContentDetailRequest.getContentDetail({
  36 + contentId: this.contentId,
  37 + relId: this.relId,
  38 + relType: this.relType
  39 + }).then((resDTO: WDHttp.ResponseDTO<ContentDetailDTO[]>) => {
  40 + if (!resDTO) {
  41 + Logger.error(TAG, 'getContentDetailData then resDTO is empty');
  42 + return
  43 + }
  44 + // Logger.info(TAG, "getNavData then,navResDTO.timeStamp:" + navResDTO.timeStamp);
  45 + if (!resDTO.data || resDTO.data.length == 0) {
  46 + Logger.error(TAG, `getContentDetailData then body is empty`);
  47 + return
  48 + }
  49 + this.title = resDTO.data[0].newsTitle
  50 + this.url = resDTO.data[0].videoInfo[0].videoUrl
  51 + this.canStart = true;
  52 + this.message = '';
  53 + }).catch((err: BusinessError) => {
  54 + Logger.error(TAG, `getContentDetailData catch, error.code : ${err.code}, error.message:${err.message}`);
  55 + // todo:
  56 + // this.title = '旅途平安!这首歌送给即将启程回家的你'
  57 + // this.url = 'https://cdnjdout.aikan.pdnews.cn/zhbj-20240127/vod/content/output/f45ef51bb33f4ffd9458f8b386aa3227_opt.mp4'
  58 + this.canStart = false;
  59 + this.message = '获取播放地址错误';
  60 + })
  61 + .finally(() => {
  62 + Logger.debug(TAG, `getContentDetailData finally`);
  63 + })
  64 + }
  65 +}
  1 +{
  2 + "module": {
  3 + "name": "wdDetailPlayShortVideo",
  4 + "type": "shared",
  5 + "description": "$string:shared_desc",
  6 + "deviceTypes": [
  7 + "phone",
  8 + "tablet",
  9 + "2in1"
  10 + ],
  11 + "deliveryWithInstall": true,
  12 + "pages": "$profile:main_pages"
  13 + }
  14 +}
  1 +{
  2 + "color": [
  3 + {
  4 + "name": "index_tab_selected_font_color",
  5 + "value": "#007DFF"
  6 + },
  7 + {
  8 + "name": "divider_color",
  9 + "value": "#D3D3D3"
  10 + },
  11 + {
  12 + "name": "track_color",
  13 + "value": "#888888"
  14 + },
  15 + {
  16 + "name": "speed_text_color",
  17 + "value": "#DDDDDD"
  18 + },
  19 + {
  20 + "name": "detail_tab_bar_select",
  21 + "value": "#333333"
  22 + },
  23 + {
  24 + "name": "detail_tab_bar_unselect",
  25 + "value": "#666666"
  26 + }
  27 + ]
  28 +}
  1 +{
  2 + "float": [
  3 + {
  4 + "name": "control_image_width",
  5 + "value": "24vp"
  6 + },
  7 + {
  8 + "name": "font_size_14",
  9 + "value": "14fp"
  10 + },
  11 + {
  12 + "name": "title_popup_image_size",
  13 + "value": "24vp"
  14 + },
  15 + {
  16 + "name": "back_image_width",
  17 + "value": "16vp"
  18 + },
  19 + {
  20 + "name": "title_popup_text_left",
  21 + "value": "8vp"
  22 + },
  23 + {
  24 + "name": "title_popup_divider_left",
  25 + "value": "47vp"
  26 + },
  27 + {
  28 + "name": "title_popup_font_size",
  29 + "value": "16fp"
  30 + },
  31 + {
  32 + "name": "title_dialog_font_size",
  33 + "value": "16fp"
  34 + },
  35 + {
  36 + "name": "text_border_radius",
  37 + "value": "8vp"
  38 + },
  39 + {
  40 + "name": "main_margin",
  41 + "value": "14vp"
  42 + },
  43 + {
  44 + "name": "text_right_bottom_font",
  45 + "value": "12vp"
  46 + }
  47 + ]
  48 +}
  1 +{
  2 + "string": [
  3 + {
  4 + "name": "shared_desc",
  5 + "value": "description"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "src": [
  3 + "pages/DetailPlayShortVideoPage"
  4 + ]
  5 +}
  1 +import localUnitTest from './LocalUnit.test';
  2 +
  3 +export default function testsuite() {
  4 + localUnitTest();
  5 +}
  1 +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
  2 +
  3 +export default function localUnitTest() {
  4 + describe('localUnitTest',() => {
  5 + // Defines a test suite. Two parameters are supported: test suite name and test suite function.
  6 + beforeAll(() => {
  7 + // Presets an action, which is performed only once before all test cases of the test suite start.
  8 + // This API supports only one parameter: preset action function.
  9 + });
  10 + beforeEach(() => {
  11 + // Presets an action, which is performed before each unit test case starts.
  12 + // The number of execution times is the same as the number of test cases defined by **it**.
  13 + // This API supports only one parameter: preset action function.
  14 + });
  15 + afterEach(() => {
  16 + // Presets a clear action, which is performed after each unit test case ends.
  17 + // The number of execution times is the same as the number of test cases defined by **it**.
  18 + // This API supports only one parameter: clear action function.
  19 + });
  20 + afterAll(() => {
  21 + // Presets a clear action, which is performed after all test cases of the test suite end.
  22 + // This API supports only one parameter: clear action function.
  23 + });
  24 + it('assertContain', 0, () => {
  25 + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
  26 + let a = 'abc';
  27 + let b = 'b';
  28 + // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
  29 + expect(a).assertContain(b);
  30 + expect(a).assertEqual(a);
  31 + });
  32 + });
  33 +}
  1 +/node_modules
  2 +/oh_modules
  3 +/.preview
  4 +/build
  5 +/.cxx
  6 +/.test
  1 +export { DetailPlayVodPage } from './src/main/ets/pages/DetailPlayVodPage'
  1 +{
  2 + "apiType": "stageMode",
  3 + "buildOption": {
  4 + "arkOptions": {
  5 + // "apPath": "./modules.ap" /* Profile used for profile-guided optimization (PGO), a compiler optimization technique to improve app runtime performance. */
  6 + }
  7 + },
  8 + "buildOptionSet": [
  9 + {
  10 + "name": "release",
  11 + "arkOptions": {
  12 + "obfuscation": {
  13 + "ruleOptions": {
  14 + "enable": true,
  15 + "files": [
  16 + "./obfuscation-rules.txt"
  17 + ]
  18 + }
  19 + }
  20 + }
  21 + },
  22 + ],
  23 + "targets": [
  24 + {
  25 + "name": "default"
  26 + }
  27 + ]
  28 +}
  1 +import { hspTasks } from '@ohos/hvigor-ohos-plugin';
  2 +
  3 +export default {
  4 + system: hspTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
  5 + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
  6 +}
  1 +# Define project specific obfuscation rules here.
  2 +# You can include the obfuscation configuration files in the current module's build-profile.json5.
  3 +#
  4 +# For more details, see
  5 +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
  6 +
  7 +# Obfuscation options:
  8 +# -disable-obfuscation: disable all obfuscations
  9 +# -enable-property-obfuscation: obfuscate the property names
  10 +# -enable-toplevel-obfuscation: obfuscate the names in the global scope
  11 +# -compact: remove unnecessary blank spaces and all line feeds
  12 +# -remove-log: remove all console.* statements
  13 +# -print-namecache: print the name cache that contains the mapping from the old names to new names
  14 +# -apply-namecache: reuse the given cache file
  15 +
  16 +# Keep options:
  17 +# -keep-property-name: specifies property names that you want to keep
  18 +# -keep-global-name: specifies names that you want to keep in the global scope
  1 +{
  2 + "name": "wddetailplayvod",
  3 + "version": "1.0.0",
  4 + "description": "Please describe the basic information.",
  5 + "main": "Index.ets",
  6 + "author": "",
  7 + "license": "Apache-2.0",
  8 + "dependencies": {
  9 + "wdBean": "file:../wdBean",
  10 + "wdPlayer": "file:../wdPlayer",
  11 + "wdKit": "file:../wdKit",
  12 + "wdDetailPlayApi": "file:../wdDetailPlayApi",
  13 + "wdRouter": "file:../wdRouter"
  14 + }
  15 +}
  1 +@Entry
  2 +@Component
  3 +export struct DetailPlayVodPage {
  4 + @State message: string = 'Detail Play Vod Page';
  5 +
  6 + build() {
  7 + Row() {
  8 + Column() {
  9 + Text(this.message)
  10 + .fontSize(50)
  11 + .fontWeight(FontWeight.Bold)
  12 + }
  13 + .width('100%')
  14 + }
  15 + .height('100%')
  16 + }
  17 +}
  1 +{
  2 + "module": {
  3 + "name": "wdDetailPlayVod",
  4 + "type": "shared",
  5 + "description": "$string:shared_desc",
  6 + "deviceTypes": [
  7 + "phone",
  8 + "tablet",
  9 + "2in1"
  10 + ],
  11 + "deliveryWithInstall": true,
  12 + "pages": "$profile:main_pages"
  13 + }
  14 +}
  1 +{
  2 + "color": [
  3 + {
  4 + "name": "white",
  5 + "value": "#FFFFFF"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "float": [
  3 + {
  4 + "name": "control_image_width",
  5 + "value": "24vp"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "string": [
  3 + {
  4 + "name": "shared_desc",
  5 + "value": "description"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "src": [
  3 + "pages/DetailPlayVodPage"
  4 + ]
  5 +}
  1 +import localUnitTest from './LocalUnit.test';
  2 +
  3 +export default function testsuite() {
  4 + localUnitTest();
  5 +}
  1 +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
  2 +
  3 +export default function localUnitTest() {
  4 + describe('localUnitTest',() => {
  5 + // Defines a test suite. Two parameters are supported: test suite name and test suite function.
  6 + beforeAll(() => {
  7 + // Presets an action, which is performed only once before all test cases of the test suite start.
  8 + // This API supports only one parameter: preset action function.
  9 + });
  10 + beforeEach(() => {
  11 + // Presets an action, which is performed before each unit test case starts.
  12 + // The number of execution times is the same as the number of test cases defined by **it**.
  13 + // This API supports only one parameter: preset action function.
  14 + });
  15 + afterEach(() => {
  16 + // Presets a clear action, which is performed after each unit test case ends.
  17 + // The number of execution times is the same as the number of test cases defined by **it**.
  18 + // This API supports only one parameter: clear action function.
  19 + });
  20 + afterAll(() => {
  21 + // Presets a clear action, which is performed after all test cases of the test suite end.
  22 + // This API supports only one parameter: clear action function.
  23 + });
  24 + it('assertContain', 0, () => {
  25 + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
  26 + let a = 'abc';
  27 + let b = 'b';
  28 + // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
  29 + expect(a).assertContain(b);
  30 + expect(a).assertEqual(a);
  31 + });
  32 + });
  33 +}
@@ -15,3 +15,7 @@ export { LazyDataSource } from './src/main/ets/utils/LazyDataSource' @@ -15,3 +15,7 @@ export { LazyDataSource } from './src/main/ets/utils/LazyDataSource'
15 export { BreakpointSystem, BreakPointType } from './src/main/ets/utils/BreakPointSystem'; 15 export { BreakpointSystem, BreakPointType } from './src/main/ets/utils/BreakPointSystem';
16 16
17 export { ToastUtils } from './src/main/ets/utils/ToastUtils'; 17 export { ToastUtils } from './src/main/ets/utils/ToastUtils';
  18 +
  19 +export { WindowModel } from './src/main/ets/utils/WindowModel'
  20 +
  21 +export { SPHelper } from './src/main/ets/utils/SPHelper'
  1 +import data_preferences from '@ohos.data.preferences';
  2 +
  3 +/*
  4 +// SPHelper.default.get("key1", "defValue1").then((value1) => {
  5 +// this.message = value1.toString();
  6 +// })
  7 +
  8 +// let value2: string = await SPHelper.default.get("key2", "defValue2");
  9 +// this.message = result;
  10 +*
  11 +* 单例模式
  12 + */
  13 +export class SPHelper {
  14 + private static context: Context;
  15 + private static spFilename: string = '__SPHelper';
  16 +
  17 + static init(context: Context) {
  18 + SPHelper.context = context;
  19 + }
  20 +
  21 + static setSpFilename(spFilename: string) {
  22 + SPHelper.spFilename = spFilename;
  23 + }
  24 +
  25 + // 静态属性
  26 + static default: SPHelper = new SPHelper();
  27 +
  28 + // 私有构造函数
  29 + private constructor() {
  30 + }
  31 +
  32 + private async getVideoPreferences(): Promise<data_preferences.Preferences> {
  33 + let preferences: data_preferences.Preferences = await data_preferences.getPreferences(SPHelper.context, SPHelper.spFilename);
  34 + return preferences;
  35 + }
  36 +
  37 + private getVideoPreferencesSync(): data_preferences.Preferences {
  38 + let options: data_preferences.Options = { name: SPHelper.spFilename };
  39 + let preferences: data_preferences.Preferences = data_preferences.getPreferencesSync(SPHelper.context, options);
  40 + return preferences;
  41 + }
  42 +
  43 + async save(key: string, value: data_preferences.ValueType) {
  44 + const preferences: data_preferences.Preferences = await this.getVideoPreferences();
  45 + await preferences.put(key, value)
  46 + await preferences.flush()
  47 + }
  48 +
  49 + saveSync(key: string, value: data_preferences.ValueType) {
  50 + const preferences: data_preferences.Preferences = this.getVideoPreferencesSync();
  51 + preferences.putSync(key, value)
  52 + preferences.flush() // todo:Asynchronously
  53 + }
  54 +
  55 + async get(key: string, defValue: data_preferences.ValueType): Promise<data_preferences.ValueType> {
  56 + const preferences: data_preferences.Preferences = await this.getVideoPreferences();
  57 + return await preferences.get(key, defValue);
  58 + }
  59 +
  60 + getSync(key: string, defValue: data_preferences.ValueType): data_preferences.ValueType {
  61 + const preferences: data_preferences.Preferences = this.getVideoPreferencesSync();
  62 + return preferences.getSync(key, defValue);
  63 + }
  64 +
  65 + async has(key: string): Promise<boolean> {
  66 + const preferences: data_preferences.Preferences = await this.getVideoPreferences();
  67 + return await preferences.has(key);
  68 + }
  69 +
  70 + hasSync(key: string): boolean {
  71 + const preferences: data_preferences.Preferences = this.getVideoPreferencesSync();
  72 + return preferences.hasSync(key);
  73 + }
  74 +
  75 + async delete(key: string) {
  76 + const preferences: data_preferences.Preferences = await this.getVideoPreferences();
  77 + preferences.delete(key).then(async () => {
  78 + await preferences.flush();
  79 + }).catch((err: Error) => {
  80 + // Logger.error(TAG, 'Failed to delete the key. Cause: ' + err);
  81 + });
  82 + }
  83 +
  84 + deleteSync(key: string) {
  85 + const preferences: data_preferences.Preferences = this.getVideoPreferencesSync();
  86 + preferences.deleteSync(key)
  87 + preferences.flush(); // todo:Asynchronously
  88 + }
  89 +
  90 + async clearSync() {
  91 + this.getVideoPreferences().then(async (preferences: data_preferences.Preferences) => {
  92 + preferences.clearSync()
  93 + await preferences.flush()
  94 + }).catch((err: Error) => {
  95 + // Logger.error(TAG, 'get the preferences failed, Cause: ' + err);
  96 + });
  97 + }
  98 +
  99 + // clearSync() {
  100 + // let preferences: data_preferences.Preferences = this.getVideoPreferencesSync()
  101 + // preferences.clearSync()
  102 + // preferences.flush()
  103 + // }
  104 +}
  1 +import window from '@ohos.window';
  2 +import { BusinessError } from '@ohos.base';
  3 +import deviceInfo from '@ohos.deviceInfo'
  4 +import display from '@ohos.display';
  5 +
  6 +export class Size {
  7 + width: number = 0
  8 + height: number = 0
  9 +
  10 + constructor(width: number, height: number) {
  11 + this.width = width;
  12 + this.height = height;
  13 + }
  14 +}
  15 +
  16 +export class WindowModel {
  17 + private windowStage?: window.WindowStage;
  18 +
  19 + static shared: WindowModel = new WindowModel()
  20 +
  21 + static TAG = "WindowModel";
  22 +
  23 + setWindowStage(windowStage: window.WindowStage) {
  24 + this.windowStage = windowStage;
  25 + }
  26 +
  27 + setMainWindowFullScreen(fullScreen: boolean) {
  28 + if (deviceInfo.deviceType != "phone") {
  29 + return
  30 + }
  31 + if (this.windowStage === undefined) {
  32 + return;
  33 + }
  34 + this.windowStage.getMainWindow((err, windowClass: window.Window) => {
  35 + if (err.code) {
  36 + return;
  37 + }
  38 + windowClass.setWindowLayoutFullScreen(fullScreen, (err) => {
  39 + if (err.code) {
  40 + return;
  41 + }
  42 + });
  43 + windowClass.setWindowSystemBarEnable(fullScreen ? [] : ['status', 'navigation'], (err) => {
  44 + if (err.code) {
  45 + return;
  46 + }
  47 + });
  48 + });
  49 + }
  50 +
  51 + getWindowSize(): Promise<Size> {
  52 + return new Promise((resolve, reject) => {
  53 + if (!this.windowStage) {
  54 + let dis = display.getDefaultDisplaySync();
  55 + reject(new Size(dis.width, dis.height))
  56 + return;
  57 + }
  58 + let rect = this.windowStage.getMainWindowSync().getWindowProperties().windowRect;
  59 + resolve(new Size(rect.width, rect.height));
  60 + });
  61 + }
  62 +
  63 + setWindowKeepScreenOn(isKeepScreenOn: boolean) {
  64 + this.windowStage?.getMainWindow((err, windowClass: window.Window) => {
  65 + windowClass.setWindowKeepScreenOn(isKeepScreenOn, (err: BusinessError) => {
  66 + const errCode: number = err.code;
  67 + if (errCode) {
  68 + console.error(WindowModel.TAG +'设置屏幕常亮:' + isKeepScreenOn + ',失败: ' + JSON.stringify(err));
  69 + return;
  70 + }
  71 + console.info(WindowModel.TAG +'设置屏幕常亮:' + isKeepScreenOn + ",成功");
  72 + })
  73 + })
  74 + }
  75 +
  76 + /**
  77 + * 设置窗口的显示方向属性
  78 + * @param orientation
  79 + */
  80 + setPreferredOrientation(orientation: window.Orientation): Promise<void> {
  81 + return new Promise((resolve, reject) => {
  82 + if (!this.windowStage) {
  83 + console.error('Failed, the main window is empty');
  84 + reject("Failed, the main window is empty")
  85 + return;
  86 + }
  87 + this.windowStage.getMainWindow((err: BusinessError, windowClass: window.Window) => {
  88 + if (err.code) {
  89 + console.error('Failed to obtain the main window. Cause: ' + err.message);
  90 + reject(err)
  91 + return;
  92 + }
  93 + // 2.设置窗口的显示方向属性,使用Promise异步回调。
  94 + windowClass.setPreferredOrientation(orientation).then(() => {
  95 + console.info('Succeeded in setting the window orientation.');
  96 + resolve()
  97 + }).catch((err: BusinessError) => {
  98 + console.error('Failed to set the window orientation. Cause: ' + err.message);
  99 + reject(err)
  100 + });
  101 + });
  102 + })
  103 + }
  104 +}
  105 +
1 export { ResponseDTO } from './src/main/ets/bean/ResponseDTO' 1 export { ResponseDTO } from './src/main/ets/bean/ResponseDTO'
  2 +
2 export { ResponseVO } from './src/main/ets/bean/ResponseVO' 3 export { ResponseVO } from './src/main/ets/bean/ResponseVO'
  4 +
3 export { ResultCode } from './src/main/ets/bean/ResultCode' 5 export { ResultCode } from './src/main/ets/bean/ResultCode'
4 6
5 export { WDHttp } from './src/main/ets/http/WDHttp' 7 export { WDHttp } from './src/main/ets/http/WDHttp'
1 import http from '@ohos.net.http'; 1 import http from '@ohos.net.http';
2 import { BusinessError } from '@ohos.base'; 2 import { BusinessError } from '@ohos.base';
3 import ArrayList from '@ohos.util.ArrayList'; 3 import ArrayList from '@ohos.util.ArrayList';
4 -import {Logger} from 'wdKit'; 4 +import { Logger } from 'wdKit';
5 import { HttpUtils } from '../utils/HttpUtils'; 5 import { HttpUtils } from '../utils/HttpUtils';
6 6
7 const TAG = 'WDHttp'; 7 const TAG = 'WDHttp';
@@ -67,9 +67,11 @@ export namespace WDHttp { @@ -67,9 +67,11 @@ export namespace WDHttp {
67 67
68 // 响应结果 68 // 响应结果
69 body?: T; 69 body?: T;
  70 + data?: T;
70 71
71 // 请求响应时间戳(unix格式) 72 // 请求响应时间戳(unix格式)
72 timeStamp?: number; 73 timeStamp?: number;
  74 +
73 // timestamp?: number; 75 // timestamp?: number;
74 76
75 // 返回当前时间戳,格式:yyyyMMddHHmmss,如20230918102124 77 // 返回当前时间戳,格式:yyyyMMddHHmmss,如20230918102124
@@ -261,7 +263,7 @@ export namespace WDHttp { @@ -261,7 +263,7 @@ export namespace WDHttp {
261 } else if (!options.header['Content-Type']) { 263 } else if (!options.header['Content-Type']) {
262 options.header['Content-Type'] = 'application/json;charset=utf-8'; 264 options.header['Content-Type'] = 'application/json;charset=utf-8';
263 } 265 }
264 - let commonHeader: Record<string, string | number> = { }; 266 + let commonHeader: Record<string, string | number> = {};
265 Request.globalHeaderProviders.forEach((func) => { 267 Request.globalHeaderProviders.forEach((func) => {
266 let headers = func(); 268 let headers = func();
267 for (const obj of Object.entries(headers)) { 269 for (const obj of Object.entries(headers)) {
  1 +/node_modules
  2 +/oh_modules
  3 +/.preview
  4 +/build
  5 +/.cxx
  6 +/.test
  1 +export { WDPlayerController } from "./src/main/ets/controller/WDPlayerController"
  2 +
  3 +export { WDPlayerRenderView } from "./src/main/ets/pages/WDPlayerRenderView"
  4 +
  5 +export { PlayerConstants } from "./src/main/ets/constants/PlayerConstants"
  6 +
  7 +export { SpeedBean } from "./src/main/ets/bean/SpeedBean"
  8 +
  9 +export { DateFormatUtil } from "./src/main/ets/utils/DateFormatUtil"
  1 +{
  2 + "apiType": "stageMode",
  3 + "buildOption": {
  4 + "arkOptions": {
  5 + // "apPath": "./modules.ap" /* Profile used for profile-guided optimization (PGO), a compiler optimization technique to improve app runtime performance. */
  6 + }
  7 + },
  8 + "buildOptionSet": [
  9 + {
  10 + "name": "release",
  11 + "arkOptions": {
  12 + "obfuscation": {
  13 + "ruleOptions": {
  14 + "enable": true,
  15 + "files": [
  16 + "./obfuscation-rules.txt"
  17 + ]
  18 + }
  19 + }
  20 + }
  21 + },
  22 + ],
  23 + "targets": [
  24 + {
  25 + "name": "default"
  26 + }
  27 + ]
  28 +}
  1 +import { hspTasks } from '@ohos/hvigor-ohos-plugin';
  2 +
  3 +export default {
  4 + system: hspTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
  5 + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
  6 +}
  1 +# Define project specific obfuscation rules here.
  2 +# You can include the obfuscation configuration files in the current module's build-profile.json5.
  3 +#
  4 +# For more details, see
  5 +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
  6 +
  7 +# Obfuscation options:
  8 +# -disable-obfuscation: disable all obfuscations
  9 +# -enable-property-obfuscation: obfuscate the property names
  10 +# -enable-toplevel-obfuscation: obfuscate the names in the global scope
  11 +# -compact: remove unnecessary blank spaces and all line feeds
  12 +# -remove-log: remove all console.* statements
  13 +# -print-namecache: print the name cache that contains the mapping from the old names to new names
  14 +# -apply-namecache: reuse the given cache file
  15 +
  16 +# Keep options:
  17 +# -keep-property-name: specifies property names that you want to keep
  18 +# -keep-global-name: specifies names that you want to keep in the global scope
  1 +{
  2 + "name": "wdplayer",
  3 + "version": "1.0.0",
  4 + "description": "Please describe the basic information.",
  5 + "main": "Index.ets",
  6 + "author": "",
  7 + "license": "Apache-2.0",
  8 + "dependencies": {
  9 + }
  10 +}
  1 +export class SpeedBean {
  2 + text: string = '';
  3 + value: number = 0;
  4 +
  5 + // constructor(text: string, value: number) {
  6 + // this.text = text;
  7 + // this.value = value;
  8 + // }
  9 +}
  1 +import { SpeedBean } from '../bean/SpeedBean';
  2 +
  3 +export class PlayerConstants {
  4 + static readonly STATUS_IDLE: number = 0;
  5 + static readonly STATUS_START: number = 1;
  6 + static readonly STATUS_PAUSE: number = 2;
  7 + static readonly STATUS_STOP: number = 3;
  8 +
  9 + static readonly OPERATE_STATE: Array<string> = ['prepared','playing', 'paused', 'completed'];
  10 +
  11 + static readonly SPEED_ARRAY:SpeedBean[] = [
  12 + { text: '0.75X', value: 0 },
  13 + { text: '1.0X', value: 1 },
  14 + { text: '1.25X', value: 2 },
  15 + { text: '1.75X', value: 3 },
  16 + { text: '2.0X', value: 4 }
  17 + ];
  18 +}
  19 +
  20 +export enum AVPlayerStatus {
  21 + IDLE = 'idle',
  22 + INITIALIZED = 'initialized',
  23 + PREPARED = 'prepared',
  24 + PLAYING = 'playing',
  25 + PAUSED = 'paused',
  26 + COMPLETED = 'completed',
  27 + STOPPED = 'stopped',
  28 + RELEASED = 'released',
  29 + ERROR = 'error'
  30 +}
  31 +
  32 +export enum Events {
  33 + STATE_CHANGE = 'stateChange',
  34 + VIDEO_SIZE_CHANGE = "videoSizeChange",
  35 + TIME_UPDATE = 'timeUpdate',
  36 + ERROR = 'error'
  37 +}
  1 +import media from '@ohos.multimedia.media';
  2 +import prompt from '@ohos.promptAction';
  3 +import { Logger } from '../utils/Logger';
  4 +import { PlayerConstants, AVPlayerStatus, Events } from '../constants/PlayerConstants';
  5 +
  6 +@Observed
  7 +export class WDPlayerController {
  8 + private initPromise: Promise<void>;
  9 + private avPlayer?: media.AVPlayer;
  10 + private duration: number = 0;
  11 + private status: number = PlayerConstants.STATUS_IDLE;
  12 + private loop: boolean = false;
  13 + private url: string = '';
  14 + private surfaceId: string = ''; // 若播放音频,无需设置surfaceId
  15 + private playSpeed: number = 1;
  16 + private seekTime: number = 0;
  17 + private positionY: number = 0;
  18 + private startTime: number = 0;
  19 + public onVideoSizeChange?: (width: number, height: number) => void;
  20 + public onTimeUpdate?: (position: number, duration: number) => void;
  21 + public onVolumeUpdate?: (volume: number) => void;
  22 + public continue?: () => void;
  23 +
  24 + constructor() {
  25 + Logger.error("初始化")
  26 + this.initPromise = this.createAVPlayer();
  27 + }
  28 +
  29 + /**
  30 + * 创建 videoPlayer对象
  31 + */
  32 + private createAVPlayer(): Promise<void> {
  33 + return new Promise((resolve, reject) => {
  34 + Logger.error("开始创建")
  35 + media.createAVPlayer().then((avPlayer) => {
  36 + if (avPlayer) {
  37 + Logger.error("创建完成1")
  38 + this.avPlayer = avPlayer;
  39 + this.bindState();
  40 + resolve();
  41 + } else {
  42 + Logger.error("创建完成0")
  43 + Logger.error('[PlayVideoModel] createAvPlayer fail!');
  44 + reject();
  45 + }
  46 + });
  47 + });
  48 + }
  49 +
  50 + /**
  51 + * AVPlayer 绑定事件.
  52 + */
  53 + private bindState() {
  54 + this.avPlayer?.on(Events.STATE_CHANGE, async (state) => {
  55 + if (this.avPlayer == null) {
  56 + return
  57 + }
  58 + switch (state) {
  59 + case AVPlayerStatus.IDLE:
  60 + this.resetProgress();
  61 + this.avPlayer.url = this.url;
  62 + break;
  63 + case AVPlayerStatus.INITIALIZED:
  64 + this.avPlayer.surfaceId = this.surfaceId;
  65 + this.avPlayer?.prepare();
  66 + break;
  67 + case AVPlayerStatus.PREPARED:
  68 + this.avPlayer.videoScaleType = 0;
  69 + if (this.startTime) {
  70 + this.setSeekTime(this.startTime, SliderChangeMode.Begin);
  71 + }
  72 + this.avPlayer.play();
  73 + this.duration = this.avPlayer.duration;
  74 + break;
  75 + case AVPlayerStatus.PLAYING:
  76 + this.setBright();
  77 + this.status = PlayerConstants.STATUS_START;
  78 + this.watchStatus();
  79 + break;
  80 + case AVPlayerStatus.PAUSED:
  81 + this.status = PlayerConstants.STATUS_PAUSE;
  82 + this.watchStatus();
  83 + break;
  84 + case AVPlayerStatus.COMPLETED:
  85 + // this.titleThis.playSpeed = 1;
  86 + if (this.continue) {
  87 + this.continue();
  88 + } else {
  89 + this.duration = 0;
  90 + this.url = this.avPlayer.url || '';
  91 + this.avPlayer.reset();
  92 + }
  93 + break;
  94 + case AVPlayerStatus.RELEASED:
  95 + this.avPlayer.release();
  96 + this.status = PlayerConstants.STATUS_STOP;
  97 + this.watchStatus();
  98 + Logger.info('[PlayVideoModel] state released called')
  99 + break;
  100 + default:
  101 + Logger.info('[PlayVideoModel] unKnown state: ' + state);
  102 + break;
  103 + }
  104 + });
  105 + this.avPlayer?.on(Events.TIME_UPDATE, (time: number) => {
  106 + this.initProgress(time);
  107 + });
  108 + this.avPlayer?.on(Events.ERROR, (error) => {
  109 + this.playError(error.message);
  110 + })
  111 + this.avPlayer?.on(Events.VIDEO_SIZE_CHANGE, (width: number, height: number) => {
  112 + if (this.onVideoSizeChange) {
  113 + this.onVideoSizeChange(width, height);
  114 + }
  115 + })
  116 + }
  117 +
  118 + public setXComponentController(controller: XComponentController) {
  119 + this.surfaceId = controller.getXComponentSurfaceId()
  120 + }
  121 +
  122 + async firstPlay(url: string) {
  123 + this.url = url;
  124 + if (this.avPlayer == null) {
  125 + Logger.error("等待")
  126 + await this.initPromise;
  127 + } else {
  128 + if (this.avPlayer.state != AVPlayerStatus.IDLE) {
  129 + await this.avPlayer.stop()
  130 + await this.avPlayer.release()
  131 + this.initPromise = this.createAVPlayer();
  132 + await this.initPromise;
  133 + }
  134 + }
  135 + if (this.avPlayer == null) {
  136 + return
  137 + }
  138 + Logger.error("开始播放")
  139 + this.avPlayer.url = this.url;
  140 + }
  141 +
  142 + async release() {
  143 + if (this.avPlayer == null) {
  144 + await this.initPromise;
  145 + }
  146 + if (this.avPlayer == null) {
  147 + return
  148 + }
  149 + this.avPlayer.release();
  150 + }
  151 +
  152 + async pause() {
  153 + if (this.avPlayer == null) {
  154 + await this.initPromise;
  155 + }
  156 + if (this.avPlayer == null) {
  157 + return
  158 + }
  159 + this.avPlayer.pause();
  160 + }
  161 +
  162 + async stop() {
  163 + if (this.avPlayer == null) {
  164 + await this.initPromise;
  165 + }
  166 + if (this.avPlayer == null) {
  167 + return
  168 + }
  169 + this.avPlayer.stop();
  170 + }
  171 +
  172 + async setLoop() {
  173 + if (this.avPlayer == null) {
  174 + await this.initPromise;
  175 + }
  176 + if (this.avPlayer == null) {
  177 + return
  178 + }
  179 + this.loop = !this.loop;
  180 + }
  181 +
  182 + async setSpeed(playSpeed: number) {
  183 + if (this.avPlayer == null) {
  184 + await this.initPromise;
  185 + }
  186 + if (this.avPlayer == null) {
  187 + return
  188 + }
  189 + if (PlayerConstants.OPERATE_STATE.indexOf(this.avPlayer.state) === -1) {
  190 + return;
  191 + }
  192 + this.playSpeed = playSpeed;
  193 + this.avPlayer.setSpeed(this.playSpeed);
  194 + }
  195 +
  196 + async switchPlayOrPause() {
  197 + if (this.avPlayer == null) {
  198 + await this.initPromise;
  199 + }
  200 + if (this.avPlayer == null) {
  201 + return
  202 + }
  203 + if (this.status === PlayerConstants.STATUS_START) {
  204 + this.avPlayer.pause();
  205 + } else {
  206 + this.avPlayer.play();
  207 + }
  208 + }
  209 +
  210 + async setSeekTime(value: number, mode: SliderChangeMode) {
  211 + if (this.avPlayer == null) {
  212 + await this.initPromise;
  213 + }
  214 + if (this.avPlayer == null) {
  215 + return
  216 + }
  217 + if (mode == SliderChangeMode.Begin) {
  218 + this.seekTime = value * 1000;
  219 + this.avPlayer.seek(this.seekTime, media.SeekMode.SEEK_PREV_SYNC);
  220 + }
  221 + if (mode === SliderChangeMode.Moving) {
  222 + // this.progressThis.progressVal = value;
  223 + // this.progressThis.currentTime = DateFormatUtil.secondToTime(Math.floor(value * this.duration /
  224 + // 100 / 1000));
  225 + }
  226 + if (mode === SliderChangeMode.End) {
  227 + this.seekTime = value * this.duration / 100;
  228 + this.avPlayer.seek(this.seekTime, media.SeekMode.SEEK_PREV_SYNC);
  229 + }
  230 + }
  231 +
  232 + setBright() {
  233 + // globalThis.windowClass.setWindowBrightness(this.playerThis.bright)
  234 + }
  235 +
  236 + getStatus() {
  237 + return this.status;
  238 + }
  239 +
  240 + initProgress(time: number) {
  241 + let nowSeconds = Math.floor(time / 1000);
  242 + let totalSeconds = Math.floor(this.duration / 1000);
  243 + if (this.onTimeUpdate) {
  244 + this.onTimeUpdate(nowSeconds, totalSeconds);
  245 + }
  246 + }
  247 +
  248 + resetProgress() {
  249 + this.seekTime = 0;
  250 + // this.progressThis.currentTime = '00:00';
  251 + // this.progressThis.progressVal = 0;
  252 + }
  253 +
  254 + onVolumeActionStart(event: GestureEvent) {
  255 + this.positionY = event.offsetY;
  256 + }
  257 +
  258 + onBrightActionStart(event: GestureEvent) {
  259 + this.positionY = event.offsetY;
  260 + }
  261 +
  262 + volume: number = 1
  263 +
  264 + onVolumeActionUpdate(event: GestureEvent) {
  265 + if (!this.avPlayer) {
  266 + return
  267 + }
  268 + if (PlayerConstants.OPERATE_STATE.indexOf(this.avPlayer.state) === -1) {
  269 + return;
  270 + }
  271 + let changeVolume = (event.offsetY - this.positionY) / 300;
  272 + let currentVolume = this.volume - changeVolume;
  273 + if (currentVolume > 1) {
  274 + currentVolume = 1;
  275 + }
  276 + if (currentVolume <= 0) {
  277 + currentVolume = 0;
  278 + }
  279 + this.volume = currentVolume;
  280 + this.avPlayer?.setVolume(this.volume);
  281 + this.positionY = event.offsetY;
  282 + if (this.onVolumeUpdate) {
  283 + this.onVolumeUpdate(this.volume);
  284 + }
  285 + console.log("volume : " + this.volume)
  286 + }
  287 +
  288 + onBrightActionUpdate(event: GestureEvent) {
  289 + // if (!this.playerThis.volumeShow) {
  290 + // this.playerThis.brightShow = true;
  291 + // let changeBright = (this.positionY - event.offsetY) / globalThis.screenHeight;
  292 + // let currentBright = this.playerThis.bright + changeBright;
  293 + // let brightMinFlag = currentBright <= 0;
  294 + // let brightMaxFlag = currentBright > 1;
  295 + // this.playerThis.bright = brightMinFlag ? 0 :
  296 + // (brightMaxFlag ? 1 : currentBright);
  297 + // this.setBright();
  298 + // this.positionY = event.offsetY;
  299 + // }
  300 + }
  301 +
  302 + onActionEnd() {
  303 + setTimeout(() => {
  304 + this.positionY = 0;
  305 + }, 200);
  306 + }
  307 +
  308 + watchStatus() {
  309 + // if (this.status === PlayConstants.STATUS_START) {
  310 + // globalThis.windowClass.setWindowKeepScreenOn(true);
  311 + // } else {
  312 + // globalThis.windowClass.setWindowKeepScreenOn(false);
  313 + // }
  314 + }
  315 +
  316 + playError(msg?: string) {
  317 + prompt.showToast({
  318 + duration: 3000,
  319 + message: msg ? msg : "请检查地址输入正确且网络正常"
  320 + });
  321 + }
  322 +
  323 + setStartTime(time?: number) {
  324 + this.startTime = time ?? 0;
  325 + }
  326 +}
  1 +import componentUtils from '@ohos.arkui.componentUtils';
  2 +import { WDPlayerController } from '../controller/WDPlayerController'
  3 +import { WindowModel } from 'wdKit';
  4 +
  5 +class Size {
  6 + width: Length = "100%";
  7 + height: Length = "100%";
  8 +
  9 + constructor(width: Length, height: Length) {
  10 + this.width = width;
  11 + this.height = height;
  12 + }
  13 +}
  14 +
  15 +let insIndex: number = 0;
  16 +
  17 +class MGPlayRenderViewIns {
  18 + static intCount: number = 0;
  19 +
  20 + static add() {
  21 + MGPlayRenderViewIns.intCount++;
  22 + WindowModel.shared.setWindowKeepScreenOn(true);
  23 + console.log("add-- +1")
  24 + }
  25 +
  26 + static del() {
  27 + console.log("add-- -1")
  28 + MGPlayRenderViewIns.intCount--;
  29 + if (MGPlayRenderViewIns.intCount <= 0) {
  30 + WindowModel.shared.setWindowKeepScreenOn(false);
  31 + }
  32 + }
  33 +}
  34 +
  35 +/**
  36 + * 播放窗口组件
  37 + */
  38 +@Entry
  39 +@Component
  40 +export struct WDPlayerRenderView {
  41 + private playerController?: WDPlayerController;
  42 + private xComponentController: XComponentController = new XComponentController();
  43 + onLoad?: ((event?: object) => void);
  44 + videoWidth: number = 0
  45 + videoHeight: number = 0
  46 + @State selfSize: Size = new Size('100%', '100%');
  47 + private insId: string = "WDPlayRenderView" + insIndex;
  48 +
  49 + aboutToAppear() {
  50 + MGPlayRenderViewIns.add();
  51 +
  52 + insIndex++;
  53 +
  54 + if (!this.playerController) {
  55 + return
  56 + }
  57 +
  58 + this.playerController.onVideoSizeChange = (width: number, height: number) => {
  59 + this.videoWidth = width;
  60 + this.videoHeight = height;
  61 + this.updateLayout()
  62 + }
  63 + }
  64 +
  65 + aboutToDisappear() {
  66 + MGPlayRenderViewIns.del();
  67 + }
  68 +
  69 + build() {
  70 + Row() {
  71 + // 设置为“surface“类型时XComponent组件可以和其他组件一起进行布局和渲染。
  72 + XComponent({
  73 + id: 'xComponentId112233',
  74 + type: 'surface',
  75 + controller: this.xComponentController
  76 + })
  77 + .onLoad(async (event) => {
  78 + this.xComponentController.setXComponentSurfaceSize({
  79 + surfaceWidth: 1920,
  80 + surfaceHeight: 1080
  81 + });
  82 + this.playerController?.setXComponentController(this.xComponentController)
  83 + if (this.onLoad) {
  84 + this.onLoad(event)
  85 + }
  86 + })
  87 + .width(this.selfSize.width)
  88 + .height(this.selfSize.height)
  89 + }
  90 + .id(this.insId)
  91 + .onAreaChange(() => {
  92 + this.updateLayout()
  93 + })
  94 + .backgroundColor("#000000")
  95 + .justifyContent(FlexAlign.Center)
  96 + .height('100%')
  97 + .width('100%')
  98 + }
  99 +
  100 + updateLayout() {
  101 + let info = componentUtils.getRectangleById(this.insId);
  102 + if (info.size.width > 0 && info.size.height > 0 && this.videoHeight > 0 && this.videoWidth > 0) {
  103 + if (info.size.width / info.size.height > this.videoWidth / this.videoHeight) {
  104 + let scale = info.size.height / this.videoHeight;
  105 + this.selfSize = new Size((this.videoWidth * scale / info.size.width) * 100 + "%", '100%');
  106 + } else {
  107 + let scale = info.size.width / this.videoWidth;
  108 + this.selfSize = new Size('100%', (this.videoHeight * scale / info.size.height) * 100 + "%");
  109 + }
  110 + }
  111 + }
  112 +}
  1 +import { PlayerConstants } from '../constants/PlayerConstants'
  2 +
  3 +export class DateFormatUtil {
  4 +
  5 + static secondToTime(seconds: number) {
  6 + let time = '00:00'
  7 + let hourUnit = 60 * 60;
  8 + let hour = Math.floor(seconds / hourUnit);
  9 + let minute = Math.floor((seconds - hour * hourUnit) / 60);
  10 + let second = seconds - hour * hourUnit - minute * 60;
  11 + if (hour > 0) {
  12 + return `${DateFormatUtil.padding(hour.toString())}${':'}${DateFormatUtil.padding(minute.toString())}${':'}${DateFormatUtil.padding(second.toString())}`;
  13 + }
  14 + if (minute > 0) {
  15 + return `${DateFormatUtil.padding(minute.toString())}${':'}${DateFormatUtil.padding(second.toString())}`;
  16 + } else {
  17 + return `${'00'}${':'}${DateFormatUtil.padding(second.toString())}`;
  18 + }
  19 + }
  20 +
  21 + static padding(num: string) {
  22 + let length = 2;
  23 + for (let len = (num.toString()).length; len < length; len = num.length) {
  24 + num = `${'0'}${num}`;
  25 + }
  26 + return num;
  27 + }
  28 +}
  29 +
  1 +import hilog from '@ohos.hilog';
  2 +
  3 +/**
  4 + * Log level define
  5 + *
  6 + * @syscap SystemCapability.HiviewDFX.HiLog
  7 + */
  8 +enum LogLevel {
  9 + DEBUG = 3,
  10 + INFO = 4,
  11 + WARN = 5,
  12 + ERROR = 6,
  13 + FATAL = 7
  14 +}
  15 +
  16 +/**
  17 + * Common log for all features.
  18 + *
  19 + * @param {string} prefix Identifies the log tag.
  20 + */
  21 +export class Logger {
  22 + private static domain: number = 0xFF00;
  23 + private static prefix: string = 'MiguVideoApp';
  24 + private static format: string = `%{public}s, %{public}s`;
  25 +
  26 + /**
  27 + * constructor.
  28 + *
  29 + * @param Prefix Identifies the log tag.
  30 + * @param domain Domain Indicates the service domain, which is a hexadecimal integer ranging from 0x0 to 0xFFFFF.
  31 + */
  32 + constructor(prefix: string = 'MiguVideoApp', domain: number = 0xFF00) {
  33 + Logger.prefix = prefix;
  34 + Logger.domain = domain;
  35 + }
  36 +
  37 + static debug(...args: string[]) {
  38 + hilog.debug(Logger.domain, Logger.prefix, Logger.format, args);
  39 + }
  40 +
  41 + static info(...args: string[]) {
  42 + hilog.info(Logger.domain, Logger.prefix, Logger.format, args);
  43 + }
  44 +
  45 + static warn(...args: string[]) {
  46 + hilog.warn(Logger.domain, Logger.prefix, Logger.format, args);
  47 + }
  48 +
  49 + static error(...args: string[]) {
  50 + hilog.error(Logger.domain, Logger.prefix, Logger.format, args);
  51 + }
  52 +
  53 + static fatal(...args: string[]) {
  54 + hilog.fatal(Logger.domain, Logger.prefix, Logger.format, args);
  55 + }
  56 +
  57 + static isLoggable(level: LogLevel) {
  58 + hilog.isLoggable(Logger.domain, Logger.prefix, level);
  59 + }
  60 +}
  61 +
  62 +export default new Logger('MiguVideoApp', 0xFF00)
  1 +{
  2 + "module": {
  3 + "name": "wdPlayer",
  4 + "type": "shared",
  5 + "description": "$string:shared_desc",
  6 + "deviceTypes": [
  7 + "phone",
  8 + "tablet",
  9 + "2in1"
  10 + ],
  11 + "deliveryWithInstall": true,
  12 + "pages": "$profile:main_pages"
  13 + }
  14 +}
  1 +{
  2 + "color": [
  3 + {
  4 + "name": "white",
  5 + "value": "#FFFFFF"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "string": [
  3 + {
  4 + "name": "shared_desc",
  5 + "value": "description"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "src": [
  3 + "pages/WDPlayerRenderView"
  4 + ]
  5 +}
  1 +import localUnitTest from './LocalUnit.test';
  2 +
  3 +export default function testsuite() {
  4 + localUnitTest();
  5 +}
  1 +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
  2 +
  3 +export default function localUnitTest() {
  4 + describe('localUnitTest',() => {
  5 + // Defines a test suite. Two parameters are supported: test suite name and test suite function.
  6 + beforeAll(() => {
  7 + // Presets an action, which is performed only once before all test cases of the test suite start.
  8 + // This API supports only one parameter: preset action function.
  9 + });
  10 + beforeEach(() => {
  11 + // Presets an action, which is performed before each unit test case starts.
  12 + // The number of execution times is the same as the number of test cases defined by **it**.
  13 + // This API supports only one parameter: preset action function.
  14 + });
  15 + afterEach(() => {
  16 + // Presets a clear action, which is performed after each unit test case ends.
  17 + // The number of execution times is the same as the number of test cases defined by **it**.
  18 + // This API supports only one parameter: clear action function.
  19 + });
  20 + afterAll(() => {
  21 + // Presets a clear action, which is performed after all test cases of the test suite end.
  22 + // This API supports only one parameter: clear action function.
  23 + });
  24 + it('assertContain', 0, () => {
  25 + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
  26 + let a = 'abc';
  27 + let b = 'b';
  28 + // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
  29 + expect(a).assertContain(b);
  30 + expect(a).assertEqual(a);
  31 + });
  32 + });
  33 +}
1 -import { WDRouterPage } from './WDRouterPage'  
2 -import { Action } from 'wdBean'  
3 import ArrayList from '@ohos.util.ArrayList'; 1 import ArrayList from '@ohos.util.ArrayList';
  2 +import { Action } from 'wdBean'
  3 +import { WDRouterPage } from './WDRouterPage'
4 4
5 interface HandleObject { 5 interface HandleObject {
6 handle: (action: Action) => (WDRouterPage | undefined) 6 handle: (action: Action) => (WDRouterPage | undefined)
@@ -8,10 +8,9 @@ interface HandleObject { @@ -8,10 +8,9 @@ interface HandleObject {
8 } 8 }
9 9
10 export class Action2Page { 10 export class Action2Page {
11 -  
12 private static handles: Record<string, ArrayList<HandleObject>> = {}; 11 private static handles: Record<string, ArrayList<HandleObject>> = {};
13 12
14 - static register(actionType: string, handle: (action: Action) => (WDRouterPage | undefined), priority: number = 0) { 13 + static register(actionType: string, handle: (action: Action) => (WDRouterPage | undefined), priority: number = 0) {
15 let handles = Action2Page.handles[actionType] ?? new ArrayList(); 14 let handles = Action2Page.handles[actionType] ?? new ArrayList();
16 let obj: HandleObject = { 15 let obj: HandleObject = {
17 handle: handle, 16 handle: handle,
@@ -49,15 +48,15 @@ export function registerRouter() { @@ -49,15 +48,15 @@ export function registerRouter() {
49 // return WDRouterPage.webLoginPage 48 // return WDRouterPage.webLoginPage
50 // }) 49 // })
51 50
52 - // Action2Page.register("JUMP_DETAIL_PAGE", (action) => {  
53 - // if (action.params?.pageID == "296ff8a4b07d457cb15b6f9e5f433cc0") {  
54 - // return WDRouterPage.tvLivePage  
55 - // }  
56 - // if (action.params?.detailPageType == 7 || action.params?.detailPageType == 8) {  
57 - // return WDRouterPage.shortVideoDetail  
58 - // }  
59 - // return WDRouterPage.playDetail  
60 - // }) 51 + Action2Page.register("JUMP_DETAIL_PAGE", (action: Action) => {
  52 + // if (action.params?.detailPageType == 2 || action.params?.detailPageType == 6) {
  53 + // return WDRouterPage.detailPlayLivePage
  54 + // }
  55 + if (action.params?.detailPageType == 7 || action.params?.detailPageType == 8) {
  56 + return WDRouterPage.detailPlayShortVideoPage
  57 + }
  58 + return WDRouterPage.detailPlayVodPage
  59 + })
61 60
62 Action2Page.register("JUMP_H5_BY_WEB_VIEW", (action) => { 61 Action2Page.register("JUMP_H5_BY_WEB_VIEW", (action) => {
63 return WDRouterPage.defaultWebPage 62 return WDRouterPage.defaultWebPage
@@ -22,4 +22,10 @@ export class WDRouterPage { @@ -22,4 +22,10 @@ export class WDRouterPage {
22 static defaultWebPage = new WDRouterPage("entry", "ets/pages/web/DefaultWebPage"); 22 static defaultWebPage = new WDRouterPage("entry", "ets/pages/web/DefaultWebPage");
23 // 电子报页面 23 // 电子报页面
24 static eNewspaper = new WDRouterPage("entry", "ets/pages/ENewspaper") 24 static eNewspaper = new WDRouterPage("entry", "ets/pages/ENewspaper")
  25 + // 短视频详情页
  26 + static detailPlayShortVideoPage = new WDRouterPage("wdDetailPlayShortVideo", "ets/pages/DetailPlayShortVideoPage");
  27 + // 点播详情页
  28 + static detailPlayVodPage = new WDRouterPage("wdDetailPlayVod", "ets/pages/DetailPlayVodPage");
  29 + // 直播详情页
  30 + static detailPlayLivePage = new WDRouterPage("wdDetailPlayLive", "ets/pages/DetailPlayLivePage");
25 } 31 }