fanmingyou3_wd

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

Showing 100 changed files with 2099 additions and 13 deletions

Too many changes to show.

To preserve performance only 100 of 100+ files are displayed.

  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 +}