From 99810a610129cd43e7e66be249a2c3043c0731b5 Mon Sep 17 00:00:00 2001
From: lon9 <815882449@qq.com>
Date: Fri, 15 Nov 2024 19:10:11 +0800
Subject: [PATCH 1/6] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=A4=E4=BA=92?=
=?UTF-8?q?=E4=BA=8B=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../base/src/main/ets/utils/AvPlayerUtil.ets | 14 ++
.../base/src/main/ets/utils/WindowUtil.ets | 12 +
.../src/main/ets/constants/HomeConstants.ets | 12 +-
.../home/src/main/ets/view/BannerView.ets | 85 +++----
.../home/src/main/ets/view/CommonView.ets | 47 ++--
.../home/src/main/ets/view/DailyVideo.ets | 67 ++---
features/home/src/main/ets/view/Home.ets | 5 +-
.../home/src/main/ets/view/HomeContent.ets | 16 +-
.../home/src/main/ets/view/HomeHeader.ets | 96 +++----
features/home/src/main/ets/view/IconView.ets | 23 +-
.../src/main/ets/view/NewVideoRelease.ets | 15 +-
.../home/src/main/ets/view/PreviousVideo.ets | 40 ++-
.../src/main/ets/view/RecommendedVideo.ets | 237 ++++++++----------
.../main/resources/base/element/color.json | 4 -
.../main/resources/base/element/float.json | 56 ++---
.../src/main/ets/view/RelatedList.ets | 6 +-
.../src/main/ets/view/SelfComment.ets | 1 +
.../src/main/ets/view/VideoDetail.ets | 18 ++
.../src/main/ets/view/VideoPlayer.ets | 36 ++-
.../main/resources/base/element/float.json | 6 +-
.../base/media/ic_public_fullscreen.svg | 16 ++
21 files changed, 381 insertions(+), 431 deletions(-)
create mode 100644 features/videoDetail/src/main/resources/base/media/ic_public_fullscreen.svg
diff --git a/commons/base/src/main/ets/utils/AvPlayerUtil.ets b/commons/base/src/main/ets/utils/AvPlayerUtil.ets
index 27f9347..bbbe6f1 100644
--- a/commons/base/src/main/ets/utils/AvPlayerUtil.ets
+++ b/commons/base/src/main/ets/utils/AvPlayerUtil.ets
@@ -191,6 +191,20 @@ export class AvPlayerUtil {
}
}
+ fastForward(): void {
+ if (this.avPlayer?.state === CommonConstants.AV_PLAYER_PLAYING_STATE || this.avPlayer?.state ===
+ CommonConstants.AV_PLAYER_PAUSED_STATE) {
+ this.avPlayer.seek(this.avPlayer.currentTime + 5000);
+ }
+ }
+
+ rewind(): void {
+ if (this.avPlayer?.state === CommonConstants.AV_PLAYER_PLAYING_STATE || this.avPlayer?.state ===
+ CommonConstants.AV_PLAYER_PAUSED_STATE) {
+ this.avPlayer.seek(this.avPlayer.currentTime - 5000);
+ }
+ }
+
formatTime(duration: number): string {
let totalSecond: number = Math.round(duration / CommonConstants.PROGRESS_THOUSAND);
let hourNum: number = Math.floor(totalSecond / CommonConstants.SECOND_IN_HOUR);
diff --git a/commons/base/src/main/ets/utils/WindowUtil.ets b/commons/base/src/main/ets/utils/WindowUtil.ets
index 775149c..cc9b2b5 100644
--- a/commons/base/src/main/ets/utils/WindowUtil.ets
+++ b/commons/base/src/main/ets/utils/WindowUtil.ets
@@ -62,6 +62,18 @@ export class WindowUtil {
this.mainWindowClass!.setWindowLayoutFullScreen(true);
}
+ maximize(): void {
+ if (this.mainWindowClass!.getWindowStatus() === window.WindowStatusType.FLOATING) {
+ this.mainWindowClass!.maximize();
+ }
+ }
+
+ recover(): void {
+ if (this.mainWindowClass!.getWindowStatus() === window.WindowStatusType.FULL_SCREEN) {
+ this.mainWindowClass!.recover();
+ }
+ }
+
getMainWindow(): window.Window | undefined {
return this.mainWindowClass;
}
diff --git a/features/home/src/main/ets/constants/HomeConstants.ets b/features/home/src/main/ets/constants/HomeConstants.ets
index 85a3fda..7b99dc5 100644
--- a/features/home/src/main/ets/constants/HomeConstants.ets
+++ b/features/home/src/main/ets/constants/HomeConstants.ets
@@ -161,10 +161,6 @@ export class HomeConstants {
* New video rows template.
*/
static readonly NEW_VIDEO_ROWS_TEMPLATE: string = '1fr';
- /**
- * Search top tab bar list space list.
- */
- static readonly SEARCH_TAB_LIST_SPACES: string[] = ['16vp', '24vp', '36vp'];
/**
* Main daily video button text.
*/
@@ -249,7 +245,7 @@ export class HomeConstants {
/**
* Video grid description height.
*/
- static readonly VIDEO_GRID_DESCRIPTION_HEIGHT: number = 114;
+ static readonly VIDEO_GRID_DESCRIPTION_HEIGHT: number = 120;
/**
* New video aspect ratio.
*/
@@ -257,11 +253,11 @@ export class HomeConstants {
/**
* New video description height.
*/
- static readonly NEW_VIDEO_DESCRIPTION_HEIGHT: number = 45;
+ static readonly NEW_VIDEO_DESCRIPTION_HEIGHT: number = 71;
/**
* Daily video description height.
*/
- static readonly DAILY_VIDEO_DESCRIPTION_HEIGHT: number[] = [103, 62];
+ static readonly DAILY_VIDEO_DESCRIPTION_HEIGHT: number[] = [103, 88, 115, 62];
/**
* Previous two ratio list.
*/
@@ -269,7 +265,7 @@ export class HomeConstants {
/**
* Banner aspect ratio list.
*/
- static readonly BANNER_RATIOS: number[] = [1.5, 1.83, 2.33];
+ static readonly BANNER_RATIOS: number[] = [1.49, 1.6, 2.1];
/**
* Home header height for sm.
*/
diff --git a/features/home/src/main/ets/view/BannerView.ets b/features/home/src/main/ets/view/BannerView.ets
index e80f754..9981a4a 100644
--- a/features/home/src/main/ets/view/BannerView.ets
+++ b/features/home/src/main/ets/view/BannerView.ets
@@ -53,23 +53,6 @@ export struct BannerView {
this.bannerDataSource = new BannerDataSource(this.bannerImgList);
}
- @Styles
- focusedStyles(): void {
- .border({
- width: $r('app.float.focus_radius_width'),
- color: $r('app.color.focus_radius_color'),
- radius: $r('app.float.banner_focus_radius')
- })
- }
-
- @Styles
- normalStyles(): void {
- .border({
- width: 0,
- radius: $r('app.float.banner_focus_radius')
- })
- }
-
build() {
Column() {
// Banner layout.
@@ -77,24 +60,19 @@ export struct BannerView {
Swiper() {
LazyForEach(this.bannerDataSource, (item: Banner, index: number) => {
Stack() {
- Image(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ?
- item.getBannerImg().getImgSrcSm() :
- item.getBannerImg().getImgSrc())
+ Image(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ? item.getBannerImg().getImgSrcSm() :
+ item.getBannerImg().getImgSrc())
.objectFit(ImageFit.Fill)
.borderRadius($r('app.float.banner_img_border_radius'))
.height(CommonConstants.FULL_PERCENT)
.width(CommonConstants.FULL_PERCENT)
.focusable(true)
- .groupDefaultFocus(index === 0 ? true : false)
- .stateStyles({
- normal: this.normalStyles,
- focused: this.focusedStyles
- })
+ .zIndex(1)
Column() {
Text(item.getDescription().getIsLeftCenter().getValue(this.currentWidthBreakpoint) ?
`${item.getDescription().getDescription()[0]}${item.getDescription().getDescription()[1]}` :
- item.getDescription().getDescription()[0])
+ item.getDescription().getDescription()[0])
.fontSize(item.getDescription().getFontSize().getValue(this.currentWidthBreakpoint)[0])
.fontWeight(item.getDescription().getFontWeight()[0])
.lineHeight(item.getDescription().getLineHeight().getValue(this.currentWidthBreakpoint)[0])
@@ -125,26 +103,35 @@ export struct BannerView {
}
.padding({
left: !item.getDescription().getIsLeftCenter().getValue(this.currentWidthBreakpoint) ?
- item.getDescription().getLeftPosition().getValue(this.currentWidthBreakpoint)[0] : '0',
+ item.getDescription().getLeftPosition().getValue(this.currentWidthBreakpoint)[0] : '0',
top: !item.getDescription().getIsTopCenter().getValue(this.currentWidthBreakpoint) ?
- item.getDescription().getTopPosition().getValue(this.currentWidthBreakpoint)[0] : '0'
+ item.getDescription().getTopPosition().getValue(this.currentWidthBreakpoint)[0] : '0'
})
.alignItems(item.getDescription().getIsLeftCenter().getValue(this.currentWidthBreakpoint) ?
- HorizontalAlign.Center : HorizontalAlign.Start)
+ HorizontalAlign.Center : HorizontalAlign.Start)
.justifyContent(!item.getDescription().getIsTopCenter().getValue(this.currentWidthBreakpoint) ?
- FlexAlign.Start : FlexAlign.Center)
+ FlexAlign.Start : FlexAlign.Center)
.height(CommonConstants.FULL_PERCENT)
.width(CommonConstants.FULL_PERCENT)
+ .zIndex(2)
}
.height(item.getBannerImg().getHeight().getValue(this.currentWidthBreakpoint))
.width(CommonConstants.FULL_PERCENT)
// The width and height vary with the container assembly and the aspect ratio remains unchanged.
.aspectRatio(new BreakpointType(HomeConstants.BANNER_RATIOS[0], HomeConstants.BANNER_RATIOS[1],
HomeConstants.BANNER_RATIOS[2]).getValue(this.currentWidthBreakpoint))
+ .groupDefaultFocus(index === 0 ? true : false)
+ .focusable(true)
+ .padding({
+ top: $r('app.float.banner_margin'),
+ bottom: $r('app.float.banner_margin'),
+ left: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ? $r('app.float.banner_padding_sm') : 0,
+ right: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ? $r('app.float.banner_padding_sm') : 0
+ })
}, (item: Banner, index: number) => index + JSON.stringify(item))
}
- .tabIndex(getTabIndex(HomeConstants.DIRECTION_LIST[2]))
.index(2)
+ .tabIndex(getTabIndex(HomeConstants.DIRECTION_LIST[2]))
.displayCount(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ? 1 : HomeConstants.TWO)
// Interval between banner images
.itemSpace(HomeConstants.SWIPER_ITEM_SPACE)
@@ -155,13 +142,13 @@ export struct BannerView {
.selectedItemWidth($r('app.float.swiper_selected_item_width'))
.selectedItemHeight($r('app.float.swiper_item_size'))
.color($r('app.color.swiper_indicator'))
+ .bottom($r('app.float.indicator_bottom'))
.selectedColor(Color.White) : false
)
.loop(true)
.width(CommonConstants.FULL_PERCENT)
.visibility((this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG) &&
- (this.currentTopIndex === 1) ?
- Visibility.None : Visibility.Visible)
+ (this.currentTopIndex === 1) ? Visibility.None : Visibility.Visible)
.effectMode(EdgeEffect.None)
// The sizes of the front and rear banners on the MD and LG devices are different.
.prevMargin(new BreakpointType($r('app.float.swiper_prev_next_margin_sm'),
@@ -207,24 +194,13 @@ export struct BannerView {
.height(this.getBannerNewHeight(this.windowWidth))
.width(CommonConstants.FULL_PERCENT)
.visibility((this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG) &&
- (this.currentTopIndex === 1) ?
- Visibility.Visible : Visibility.None)
+ (this.currentTopIndex === 1) ? Visibility.Visible : Visibility.None)
.padding({
left: $r('app.float.banner_padding_sm'),
right: $r('app.float.banner_padding_sm')
})
}
.width(CommonConstants.FULL_PERCENT)
- .margin({
- top: $r('app.float.banner_margin'),
- bottom: $r('app.float.banner_margin')
- })
- .padding({
- left: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ? $r('app.float.banner_padding_sm') :
- '0',
- right: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ? $r('app.float.banner_padding_sm') :
- '0'
- })
.visibility(this.currentTopIndex === 2 ? Visibility.None : Visibility.Visible)
Row() {
@@ -329,7 +305,7 @@ struct BannerText {
build() {
Stack() {
Image(((this.index === 0) || (this.index === 1)) ? this.banner.getBannerImg().getImgSrcSm() :
- this.banner.getBannerImg().getImgSrc())
+ this.banner.getBannerImg().getImgSrc())
.width(CommonConstants.FULL_PERCENT)
.height(CommonConstants.FULL_PERCENT)
.objectFit(ImageFit.Fill)
@@ -338,14 +314,13 @@ struct BannerText {
Column() {
Text(this.banner.getDescription().getIsLeftCenter().getValue(this.currentWidthBreakpoint) ?
`${this.banner.getDescription().getDescription()[0]}${this.banner.getDescription().getDescription()[1]}` :
- this.banner.getDescription().getDescription()[0])
+ this.banner.getDescription().getDescription()[0])
.fontSize(this.banner.getDescription().getFontSize().getValue(this.currentWidthBreakpoint)[0])
.fontWeight(this.banner.getDescription().getFontWeight()[0])
.lineHeight(this.banner.getDescription().getLineHeight().getValue(this.currentWidthBreakpoint)[0])
.fontColor(Color.White)
.letterSpacing(HomeConstants.BANNER_TEXT_LETTER_SPACES[2])
.maxLines(1)
-
Text(this.banner.getDescription().getDescription()[1])
.fontSize(this.banner.getDescription().getFontSize().getValue(this.currentWidthBreakpoint)[0])
.fontWeight(this.banner.getDescription().getFontWeight()[0])
@@ -354,29 +329,27 @@ struct BannerText {
.letterSpacing(HomeConstants.BANNER_TEXT_LETTER_SPACES[2])
.maxLines(1)
.visibility(this.banner.getDescription().getIsLeftCenter().getValue(this.currentWidthBreakpoint) ?
- Visibility.None : Visibility.Visible)
+ Visibility.None : Visibility.Visible)
Text(this.banner.getDescription().getDescription()[2])
.fontSize(this.banner.getDescription().getFontSize().getValue(this.currentWidthBreakpoint)[1])
.fontWeight(this.banner.getDescription().getFontWeight()[1])
.lineHeight(this.banner.getDescription().getLineHeight().getValue(this.currentWidthBreakpoint)[1])
.fontColor(Color.White)
- .margin({
- top: this.banner.getDescription().getTopPosition().getValue(this.currentWidthBreakpoint)[1]
- })
+ .margin({ top: this.banner.getDescription().getTopPosition().getValue(this.currentWidthBreakpoint)[1] })
.letterSpacing(HomeConstants.BANNER_TEXT_LETTER_SPACES[1])
.maxLines(1)
}
.padding({
left: !this.banner.getDescription().getIsLeftCenter().getValue(this.currentWidthBreakpoint) ?
- this.banner.getDescription().getLeftPosition().getValue(this.currentWidthBreakpoint)[0] : '0',
+ this.banner.getDescription().getLeftPosition().getValue(this.currentWidthBreakpoint)[0] : '0',
top: !this.banner.getDescription().getIsTopCenter().getValue(this.currentWidthBreakpoint) ?
- this.banner.getDescription().getTopPosition().getValue(this.currentWidthBreakpoint)[0] : '0'
+ this.banner.getDescription().getTopPosition().getValue(this.currentWidthBreakpoint)[0] : '0'
})
.alignItems(this.banner.getDescription().getIsLeftCenter().getValue(this.currentWidthBreakpoint) ?
- HorizontalAlign.Center : HorizontalAlign.Start)
+ HorizontalAlign.Center : HorizontalAlign.Start)
.justifyContent(!this.banner.getDescription().getIsTopCenter().getValue(this.currentWidthBreakpoint) ?
- FlexAlign.Start : FlexAlign.Center)
+ FlexAlign.Start : FlexAlign.Center)
.width(CommonConstants.FULL_PERCENT)
.height(CommonConstants.FULL_PERCENT)
}
diff --git a/features/home/src/main/ets/view/CommonView.ets b/features/home/src/main/ets/view/CommonView.ets
index 4b840f3..1f922e8 100644
--- a/features/home/src/main/ets/view/CommonView.ets
+++ b/features/home/src/main/ets/view/CommonView.ets
@@ -13,7 +13,7 @@
* limitations under the License.
*/
-import { BreakpointConstants, CommonConstants } from '@ohos/commons';
+import { BreakpointConstants, BreakpointType, CommonConstants } from '@ohos/commons';
import { HomeConstants } from '../constants/HomeConstants';
@Component
@@ -86,51 +86,34 @@ export struct SubtitleComponent {
private title: ResourceStr = '';
build() {
- Row() {
- Text(this.title)
- .fontSize($r('app.float.sub_title_font'))
- .fontWeight(CommonConstants.FONT_WEIGHT_500)
- .fontColor(this.currentTopIndex === 2 ? Color.White : $r('app.color.normal_font_color'))
- }
- .height($r('app.float.sub_title_row_height'))
- .width(CommonConstants.FULL_PERCENT)
- .margin({
- left: $r('app.float.sub_title_row_margin'),
- right: $r('app.float.sub_title_row_margin')
- })
+ Text(this.title)
+ .fontSize($r('app.float.sub_title_font'))
+ .fontWeight(CommonConstants.FONT_WEIGHT_500)
+ .fontColor(this.currentTopIndex === 2 ? Color.White : $r('app.color.normal_font_color'))
+ .lineHeight($r('app.float.sub_title_row_height'))
+ .width(CommonConstants.FULL_PERCENT)
+ .padding({
+ left: new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
+ $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint),
+ right: new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
+ $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint)})
}
}
@Component
export struct VideoImgComponent {
private imgSrc?: Resource;
-
- @Styles focusedStyles(): void {
- .border({
- width: $r('app.float.focus_radius_width'),
- color: $r('app.color.focus_radius_color'),
- radius: $r('app.float.banner_focus_radius')
- })
- }
-
- @Styles normalStyles(): void {
- .border({
- width: 0,
- radius: $r('app.float.banner_focus_radius')
- })
- }
+ private index: number = 0;
build() {
Image(this.imgSrc ? this.imgSrc : '')
.focusable(true)
+ .groupDefaultFocus(this.index === 0 ? true : false)
.objectFit(ImageFit.Fill)
.width(CommonConstants.FULL_PERCENT)
.height(CommonConstants.FULL_PERCENT)
.draggable(false)
- .stateStyles({
- normal: this.normalStyles,
- focused: this.focusedStyles
- })
+ .borderRadius($r('app.float.banner_focus_radius'))
}
}
diff --git a/features/home/src/main/ets/view/DailyVideo.ets b/features/home/src/main/ets/view/DailyVideo.ets
index 72d5512..781f5fa 100644
--- a/features/home/src/main/ets/view/DailyVideo.ets
+++ b/features/home/src/main/ets/view/DailyVideo.ets
@@ -18,7 +18,6 @@ import { BreakpointType } from '@ohos/commons';
import { HomeConstants } from '../constants/HomeConstants';
import { VideoImage, VideoImgViewModel } from '../viewmodel/VideoImgViewModel';
import { getTabIndex, SubtitleComponent, VideoImgComponent, VideoImgPlay, VideoImgRating } from './CommonView';
-import { display } from '@kit.ArkUI';
@Component
export struct DailyVideo {
@@ -29,27 +28,6 @@ export struct DailyVideo {
private dailyVideoImgList: VideoImage[] = new VideoImgViewModel().getDailyVideoImgList();
private mainDailyVideoImg: VideoImage = new VideoImgViewModel().getMainDailyVideoImg();
- @Styles focusedMainStyles(): void {
- .border({
- width: $r('app.float.focus_radius_width'),
- color: $r('app.color.focus_radius_color'),
- radius: {
- topLeft: $r('app.float.daily_main_focus_radius'),
- topRight: $r('app.float.daily_main_focus_radius')
- }
- })
- }
-
- @Styles normalMainStyles(): void {
- .border({
- width: 0,
- radius: {
- topLeft: $r('app.float.daily_main_focus_radius'),
- topRight: $r('app.float.daily_main_focus_radius')
- }
- })
- }
-
build() {
Column() {
SubtitleComponent({ title: HomeConstants.HOME_SUB_TITLES[1] })
@@ -60,11 +38,7 @@ export struct DailyVideo {
md: BreakpointConstants.GRID_ROW_COLUMNS[0],
lg: BreakpointConstants.GRID_ROW_COLUMNS[0]
},
- gutter: $r('app.float.grid_row_gutter'),
- breakpoints: {
- value: ['320vp', '600vp', '840vp'],
- reference: BreakpointsReference.WindowSize
- }
+ gutter: $r('app.float.grid_row_gutter')
}) {
// Main video section.
GridCol({
@@ -82,9 +56,9 @@ export struct DailyVideo {
.width(CommonConstants.FULL_PERCENT)
.height(CommonConstants.FULL_PERCENT)
.draggable(false)
- .stateStyles({
- normal: this.normalMainStyles,
- focused: this.focusedMainStyles
+ .borderRadius({
+ topLeft: $r('app.float.daily_main_focus_radius'),
+ topRight: $r('app.float.daily_main_focus_radius')
})
VideoImgPlay()
@@ -188,6 +162,15 @@ export struct DailyVideo {
.borderRadius($r('app.float.card_radius'))
.backgroundColor($r('app.color.home_component_background'))
}
+ .padding({
+ top: $r('app.float.sub_title_row_margin'),
+ bottom: this.currentWidthBreakpoint !== BreakpointConstants.BREAKPOINT_SM ?
+ $r('app.float.sub_title_row_margin') : 0,
+ left: new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
+ $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint),
+ right: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ?
+ $r('app.float.home_content_padding_sm') : 0
+ })
// Sub video section.
GridCol({
@@ -242,6 +225,16 @@ export struct DailyVideo {
.rowsTemplate(CommonConstants.VIDEO_GRID_COLUMNS[0])
.rowsGap($r('app.float.daily_grid_gap'))
.columnsGap($r('app.float.daily_grid_gap'))
+ .padding({
+ top: this.currentWidthBreakpoint !== BreakpointConstants.BREAKPOINT_SM ?
+ $r('app.float.sub_title_row_margin') : $r('app.float.focus_more_size'),
+ bottom: this.currentWidthBreakpoint !== BreakpointConstants.BREAKPOINT_SM ?
+ $r('app.float.sub_title_row_margin') : $r('app.float.focus_more_size'),
+ left: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ?
+ $r('app.float.home_content_padding_sm') : $r('app.float.focus_more_size'),
+ right: new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
+ $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint)
+ })
}
}
}
@@ -265,10 +258,18 @@ export struct DailyVideo {
}
// Calculate the height of a single image and title, and calculate the total height of the grid layout.
result = (result - HomeConstants.VIDEO_GRID_ITEM_SPACE) / HomeConstants.VIDEO_DIALOG_ASPECT_RATIO;
- if (currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM && !isMain) {
- return (result + HomeConstants.DAILY_VIDEO_DESCRIPTION_HEIGHT[0] + HomeConstants.HEIGHT_UNIT);
+ if (currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM) {
+ if (isMain) {
+ return (result + HomeConstants.DAILY_VIDEO_DESCRIPTION_HEIGHT[0] + HomeConstants.HEIGHT_UNIT);
+ } else {
+ return (result + HomeConstants.DAILY_VIDEO_DESCRIPTION_HEIGHT[2] + HomeConstants.HEIGHT_UNIT);
+ }
} else {
- return (result + HomeConstants.DAILY_VIDEO_DESCRIPTION_HEIGHT[1] + HomeConstants.HEIGHT_UNIT);
+ if (!isMain) {
+ return (result + HomeConstants.DAILY_VIDEO_DESCRIPTION_HEIGHT[1] + HomeConstants.HEIGHT_UNIT);
+ } else {
+ return (result + HomeConstants.DAILY_VIDEO_DESCRIPTION_HEIGHT[3] + HomeConstants.HEIGHT_UNIT);
+ }
}
}
}
\ No newline at end of file
diff --git a/features/home/src/main/ets/view/Home.ets b/features/home/src/main/ets/view/Home.ets
index 430cf3c..d7e16ef 100644
--- a/features/home/src/main/ets/view/Home.ets
+++ b/features/home/src/main/ets/view/Home.ets
@@ -134,11 +134,12 @@ export struct Home {
.barMode(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG ? BarMode.Scrollable : BarMode.Fixed,
{ nonScrollableLayoutStyle: LayoutStyle.ALWAYS_CENTER })
.vertical(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG)
+ .barOverlap(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG && this.currentTopIndex === 2 ? true :
+ false)
+ .barBackgroundBlurStyle(BlurStyle.NONE)
.barBackgroundColor(this.currentTopIndex === 2 && this.currentBottomIndex === 0 ?
(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG ? $r('app.color.side_bar_background') :
$r('app.color.home_content_background')) : $r('app.color.tab_background_color'))
- .barOverlap(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG && this.currentTopIndex === 2 ? true :
- false)
.onChange((index: number) => {
this.currentBottomIndex = index;
})
diff --git a/features/home/src/main/ets/view/HomeContent.ets b/features/home/src/main/ets/view/HomeContent.ets
index 4788d1e..098ad1d 100644
--- a/features/home/src/main/ets/view/HomeContent.ets
+++ b/features/home/src/main/ets/view/HomeContent.ets
@@ -13,7 +13,6 @@
* limitations under the License.
*/
-
import { deviceInfo } from '@kit.BasicServicesKit';
import { BreakpointConstants, BreakpointType, CommonConstants } from '@ohos/commons';
import { WindowUtil } from '@ohos/commons';
@@ -44,18 +43,9 @@ export struct HomeContent {
BannerView()
IconView()
RecommendedVideo()
- Column() {
- NewVideoRelease()
- DailyVideo()
- PreviousVideo()
- }
- .padding({
- left: new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
- $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint),
- right: new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
- $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint)
- })
- .width(CommonConstants.FULL_PERCENT)
+ NewVideoRelease()
+ DailyVideo()
+ PreviousVideo()
}
// Set the background image to cover the side and top tabs.
.backgroundImage(this.currentTopIndex === 2 && !this.isSearching ? new BreakpointType(
diff --git a/features/home/src/main/ets/view/HomeHeader.ets b/features/home/src/main/ets/view/HomeHeader.ets
index b90f40f..7634098 100644
--- a/features/home/src/main/ets/view/HomeHeader.ets
+++ b/features/home/src/main/ets/view/HomeHeader.ets
@@ -27,36 +27,6 @@ export struct HomeHeader {
@StorageLink('currentWidthBreakpoint') currentWidthBreakpoint: string = BreakpointConstants.BREAKPOINT_LG;
@StorageLink('currentTopIndex') currentTopIndex: number = 0;
- @Styles topFocusedStyles(): void {
- .border({
- width: $r('app.float.focus_radius_width'),
- color: $r('app.color.focus_radius_color'),
- radius: $r('app.float.text_focus_radius')
- })
- }
-
- @Styles topNormalStyles(): void {
- .border({
- width: 0,
- radius: $r('app.float.text_focus_radius')
- })
- }
-
- @Styles inputFocusedStyles(): void {
- .border({
- width: $r('app.float.focus_radius_width'),
- color: $r('app.color.focus_radius_color'),
- radius: $r('app.float.input_focus_radius')
- })
- }
-
- @Styles inputNormalStyles(): void {
- .border({
- width: 0,
- radius: $r('app.float.input_focus_radius')
- })
- }
-
build() {
Column() {
GridRow({
@@ -99,10 +69,6 @@ export struct HomeHeader {
HomeConstants.BACKGROUND_CHANGE_HEIGHT[1], HomeConstants.BACKGROUND_CHANGE_HEIGHT[2])
.getValue(this.currentWidthBreakpoint) && this.currentTopIndex === 2 ? $r('app.color.home_content_background') :
Color.Transparent)
- .padding({
- left: $r('app.float.search_top_padding'),
- right: $r('app.float.search_top_padding')
- })
}
.width(CommonConstants.FULL_PERCENT)
}
@@ -111,38 +77,35 @@ export struct HomeHeader {
TopTabBar() {
Row() {
Column() {
- List({
- space: new BreakpointType(HomeConstants.SEARCH_TAB_LIST_SPACES[0], HomeConstants.SEARCH_TAB_LIST_SPACES[1],
- HomeConstants.SEARCH_TAB_LIST_SPACES[2]).getValue(this.currentWidthBreakpoint)
- }) {
+ List() {
ForEach(HomeConstants.TOP_TOPIC_LIST, (item: string, index: number) => {
ListItem() {
- Column() {
- Text(item)
- .fontSize(this.currentTopIndex === index ? $r('app.float.search_tab_font_selected') :
- $r('app.float.search_tab_font'))
- .fontWeight(this.currentTopIndex === index ? CommonConstants.FONT_WEIGHT_700 :
- CommonConstants.FONT_WEIGHT_500)
- .width(this.currentTopIndex === index ? $r('app.float.top_text_width_selected') :
- $r('app.float.top_text_width'))
- .textAlign(TextAlign.Center)
- .fontColor(this.currentTopIndex === 2 ? Color.White : $r('app.color.normal_font_color'))
- .focusable(true)
- .groupDefaultFocus(index === 0 ? true : false)
- }
- .stateStyles({
- normal: this.topNormalStyles,
- focused: this.topFocusedStyles
- })
- .height(index === this.currentTopIndex ? $r('app.float.top_tab_list_height_selected') :
- $r('app.float.top_tab_list_height'))
+ Text(item)
+ .fontSize(this.currentTopIndex === index ? $r('app.float.search_tab_font_selected') :
+ $r('app.float.search_tab_font'))
+ .fontWeight(this.currentTopIndex === index ? CommonConstants.FONT_WEIGHT_700 :
+ CommonConstants.FONT_WEIGHT_500)
+ .width(this.currentTopIndex === index ? $r('app.float.top_text_width_selected') :
+ $r('app.float.top_text_width'))
+ .textAlign(TextAlign.Center)
+ .fontColor(this.currentTopIndex === 2 ? Color.White : $r('app.color.normal_font_color'))
+ .lineHeight(index === this.currentTopIndex ? $r('app.float.top_tab_list_height_selected') :
+ $r('app.float.top_tab_list_height'))
+ .focusable(true)
+ .groupDefaultFocus(index === 0 ? true : false)
+ .borderRadius($r('app.float.text_focus_radius'))
}
+ .align(Alignment.Center)
.margin({
top: this.currentTopIndex === index ? $r('app.float.top_tab_list_top_selected') :
$r('app.float.top_tab_list_top'),
bottom: this.currentTopIndex === index ? $r('app.float.top_tab_list_bottom_selected') :
$r('app.float.top_tab_list_bottom')
})
+ .padding({
+ left: new BreakpointType(8, 12, 18).getValue(this.currentWidthBreakpoint),
+ right: new BreakpointType(8, 12, 18).getValue(this.currentWidthBreakpoint)
+ })
.onClick(() => {
this.currentTopIndex = index;
this.scrollHeight = 0;
@@ -152,6 +115,7 @@ export struct HomeHeader {
.tabIndex(getTabIndex(HomeConstants.DIRECTION_LIST[1]))
.scrollBar(BarState.Off)
.listDirection(Axis.Horizontal)
+ .padding({ left: $r('app.float.search_top_padding') })
}
.alignItems(HorizontalAlign.Center)
.layoutWeight(1)
@@ -159,7 +123,10 @@ export struct HomeHeader {
Image($r('app.media.ic_public_more'))
.width($r('app.float.top_tab_img_size'))
.height($r('app.float.top_tab_img_size'))
- .margin({ left: $r('app.float.top_tab_img_margin') })
+ .margin({
+ left: $r('app.float.top_tab_img_margin'),
+ right: $r('app.float.top_tab_img_margin')
+ })
.fillColor(this.currentTopIndex === 2 ? Color.White : Color.Black)
.visibility(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ? Visibility.Visible : Visibility.None)
}
@@ -192,13 +159,10 @@ export struct HomeHeader {
this.isSearching = true;
}
})
- .stateStyles({
- normal: this.inputNormalStyles,
- focused: this.inputFocusedStyles
- })
.enterKeyType(EnterKeyType.Go)
.enableKeyboardOnFocus(false)
.backgroundColor(this.currentTopIndex === 2 ? $r('app.color.search_bar_background') : '')
+ .tabIndex(getTabIndex(HomeConstants.DIRECTION_LIST[1]))
// Search image.
Image(this.currentTopIndex === 2 && this.currentWidthBreakpoint ? $r("app.media.ic_public_search_white") :
@@ -207,7 +171,7 @@ export struct HomeHeader {
.height($r('app.float.search_img_size'))
.margin({ left: $r('app.float.search_img_left') })
}
- .tabIndex(getTabIndex(HomeConstants.DIRECTION_LIST[1]))
+ // .tabIndex(getTabIndex(HomeConstants.DIRECTION_LIST[1]))
.alignSelf(ItemAlign.Center)
.layoutWeight(1)
@@ -223,8 +187,10 @@ export struct HomeHeader {
.margin({ left: $r('app.float.history_img_left') })
.visibility(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ? Visibility.None : Visibility.Visible)
}
- .padding({ left: this.currentWidthBreakpoint !== BreakpointConstants.BREAKPOINT_SM ?
- $r('app.float.search_bar_row_padding') : '0' })
+ .padding({
+ left: $r('app.float.search_top_padding'),
+ right: $r('app.float.search_top_padding')
+ })
.justifyContent(FlexAlign.Center)
.width(CommonConstants.FULL_PERCENT)
.height(CommonConstants.FULL_PERCENT)
diff --git a/features/home/src/main/ets/view/IconView.ets b/features/home/src/main/ets/view/IconView.ets
index 007415e..2ad191e 100644
--- a/features/home/src/main/ets/view/IconView.ets
+++ b/features/home/src/main/ets/view/IconView.ets
@@ -26,21 +26,6 @@ export struct IconView {
@StorageLink('currentTopIndex') currentTopIndex: number = 0;
private iconList: VideoIcon[] = new IconViewModel().getIconList();
- @Styles focusedStyles(): void {
- .border({
- width: $r('app.float.focus_radius_width'),
- color: $r('app.color.focus_radius_color'),
- radius: $r('app.float.icon_focus_radius')
- })
- }
-
- @Styles normalStyles(): void {
- .border({
- width: 0,
- radius: $r('app.float.icon_focus_radius')
- })
- }
-
build() {
// Icon swiper.
Column() {
@@ -53,10 +38,7 @@ export struct IconView {
.margin({ bottom: $r('app.float.icon_img_bottom_margin') })
.focusable(true)
.groupDefaultFocus(index === 0 ? true : false)
- .stateStyles({
- normal: this.normalStyles,
- focused: this.focusedStyles
- })
+ .borderRadius($r('app.float.icon_img_radius'))
Text(item.getName())
.fontSize($r('app.float.icon_list_font_size'))
@@ -65,6 +47,7 @@ export struct IconView {
.fontColor(this.currentTopIndex === 2 ? Color.White : Color.Black)
}
.alignItems(HorizontalAlign.Center)
+ .justifyContent(FlexAlign.Center)
.height($r('app.float.icon_list_height'))
}, (item: VideoIcon) => JSON.stringify(item))
}
@@ -84,7 +67,7 @@ export struct IconView {
$r('app.float.icon_list_column_padding')
})
.margin({ top: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG ?
- $r('app.float.icon_list_column_margin_lg') : $r('app.float.icon_list_column_margin') })
+ $r('app.float.icon_list_column_margin_lg') : 0 })
.visibility(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG && this.currentTopIndex === 2 ?
Visibility.None : Visibility.Visible)
}
diff --git a/features/home/src/main/ets/view/NewVideoRelease.ets b/features/home/src/main/ets/view/NewVideoRelease.ets
index 4194d60..031e227 100644
--- a/features/home/src/main/ets/view/NewVideoRelease.ets
+++ b/features/home/src/main/ets/view/NewVideoRelease.ets
@@ -29,15 +29,16 @@ export struct NewVideoRelease {
build() {
Column() {
- SubtitleComponent({ title:HomeConstants.HOME_SUB_TITLES[0] })
+ SubtitleComponent({ title: HomeConstants.HOME_SUB_TITLES[0] })
+ .margin({ top: $r('app.float.sub_title_row_margin') })
// Video grid.
Grid() {
- ForEach(this.newVideoImgList, (item: VideoImage) => {
+ ForEach(this.newVideoImgList, (item: VideoImage, index: number) => {
GridItem() {
Column() {
Stack({ alignContent: Alignment.Bottom }) {
- VideoImgComponent({ imgSrc:item.getImgSrc() })
+ VideoImgComponent({ imgSrc:item.getImgSrc(), index: index })
VideoImgPlay()
VideoImgRating({ rating: item.getRating() })
}
@@ -54,6 +55,14 @@ export struct NewVideoRelease {
}, (item: VideoImage, index: number) => index + JSON.stringify(item))
}
.tabIndex(getTabIndex(HomeConstants.DIRECTION_LIST[5]))
+ .padding({
+ left: new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
+ $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint),
+ right: new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
+ $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint),
+ top: $r('app.float.sub_title_row_margin'),
+ bottom: $r('app.float.sub_title_row_margin')
+ })
.columnsTemplate(new BreakpointType(CommonConstants.VIDEO_GRID_COLUMNS[1], CommonConstants.VIDEO_GRID_COLUMNS[3],
CommonConstants.VIDEO_GRID_COLUMNS[4]).getValue(this.currentWidthBreakpoint))
.rowsTemplate(HomeConstants.NEW_VIDEO_ROWS_TEMPLATE)
diff --git a/features/home/src/main/ets/view/PreviousVideo.ets b/features/home/src/main/ets/view/PreviousVideo.ets
index e7ad7c7..f81d3cd 100644
--- a/features/home/src/main/ets/view/PreviousVideo.ets
+++ b/features/home/src/main/ets/view/PreviousVideo.ets
@@ -28,30 +28,10 @@ export struct PreviousVideo {
@Consume('pageInfo') pageInfo: VideoNavPathStack;
private previousVideoImgListOne: VideoImage[] = new VideoImgViewModel().getPreviousVideoOne();
- @Styles focusedStyles(): void {
- .border({
- width: $r('app.float.focus_radius_width'),
- color: $r('app.color.focus_radius_color'),
- radius: {
- topLeft: $r('app.float.previous_two_radius'),
- topRight: $r('app.float.previous_two_radius')
- }
- })
- }
-
- @Styles normalStyles(): void {
- .border({
- width: 0,
- radius: {
- topLeft: $r('app.float.previous_two_radius'),
- topRight: $r('app.float.previous_two_radius')
- }
- })
- }
-
build() {
Column() {
SubtitleComponent({ title: HomeConstants.HOME_SUB_TITLES[2] })
+ .margin({ bottom: $r('app.float.sub_title_row_margin') })
GridRow({
columns: {
@@ -77,6 +57,7 @@ export struct PreviousVideo {
.layoutWeight(CommonConstants.THREE)
.height(CommonConstants.FULL_PERCENT)
.borderRadius($r('app.float.banner_img_border_radius'))
+ .focusable(true)
Column() {
Text(item.getTitle())
@@ -114,7 +95,7 @@ export struct PreviousVideo {
.height(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG ?
$r('app.float.previous_one_button_height_lg') : $r('app.float.previous_one_button_height'))
.layoutWeight(1)
- .groupDefaultFocus(index === 0 ? true : false)
+ .focusable(true)
.onClick(() => {
this.pageInfo.setPageName(CommonConstants.PAGE_NAMES[1]);
this.pageInfo.pushPath({ name: CommonConstants.PAGE_NAMES[1] });
@@ -134,6 +115,7 @@ export struct PreviousVideo {
.height(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG ?
$r('app.float.previous_one_button_height_lg') : $r('app.float.previous_one_button_height'))
.layoutWeight(1)
+ .focusable(true)
}
.width(CommonConstants.FULL_PERCENT)
.alignItems(VerticalAlign.Bottom)
@@ -158,6 +140,7 @@ export struct PreviousVideo {
.backgroundColor($r('app.color.home_component_background'))
.borderRadius($r('app.float.card_radius'))
}
+ .focusable(true)
}, (item: VideoImage, index: number) => index + JSON.stringify(item))
ForEach(this.previousVideoImgListTwo, (item: VideoImage, index: number) => {
@@ -174,9 +157,9 @@ export struct PreviousVideo {
.aspectRatio(PreviousVideoUtil.getImgAspectRatio(this.currentWidthBreakpoint, index))
.objectFit(ImageFit.Fill)
.focusable(true)
- .stateStyles({
- normal: this.normalStyles,
- focused: this.focusedStyles
+ .borderRadius({
+ topLeft: $r('app.float.previous_two_radius'),
+ topRight: $r('app.float.previous_two_radius')
})
Column() {
@@ -220,9 +203,16 @@ export struct PreviousVideo {
}
}, (item: VideoImage, index: number) => index + JSON.stringify(item))
}
+ .focusable(true)
.onBreakpointChange((breakPoints) => {
this.previousVideoImgListTwo = new VideoImgViewModel().getPreviousVideoTwo(breakPoints);
})
+ .padding({
+ left: new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
+ $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint),
+ right: new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
+ $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint)
+ })
}
.tabIndex(getTabIndex(HomeConstants.DIRECTION_LIST[8]))
.padding({ left: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG && this.currentTopIndex === 2 ?
diff --git a/features/home/src/main/ets/view/RecommendedVideo.ets b/features/home/src/main/ets/view/RecommendedVideo.ets
index 4a21ce3..2aa9fdb 100644
--- a/features/home/src/main/ets/view/RecommendedVideo.ets
+++ b/features/home/src/main/ets/view/RecommendedVideo.ets
@@ -30,152 +30,125 @@ export struct RecommendedVideo {
@StorageLink('currentTopIndex') currentTopIndex: number = 0;
@State isShowingRecommendBorder: boolean = false;
@State currentRecommendFocusIndex: number = 0;
- @State recommendedCurrentVideo: number = -1;
@State videoImgList: VideoImage[] = new VideoImgViewModel().getVideoImgList();
private videoDialogController?: CustomDialogController;
private windowUtil?: WindowUtil;
- @Styles focusedStyles(): void {
- .border({
- width: $r('app.float.focus_radius_width'),
- color: $r('app.color.focus_radius_color'),
- radius: $r('app.float.banner_focus_radius')
- })
- }
-
- @Styles normalStyles(): void {
- .border({
- width: 0,
- radius: $r('app.float.banner_focus_radius')
- })
- }
-
build() {
- Column() {
- // Video grid layout.
- Grid() {
- ForEach(this.videoImgList, (item: VideoImage, index: number) => {
- GridItem() {
- Column() {
- Stack({ alignContent: Alignment.Center }) {
- Image(item.getImgSrc())
- .focusable(true)
- .groupDefaultFocus(index === 0 ? true : false)
- .objectFit(ImageFit.Fill)
- .width(this.recommendedCurrentVideo === index ? HomeConstants.PERCENT_HUNDRED_AND_FIVE :
- CommonConstants.FULL_PERCENT)
- .height(this.recommendedCurrentVideo === index ? HomeConstants.PERCENT_HUNDRED_AND_FIVE :
- CommonConstants.FULL_PERCENT)
- .draggable(false)
- .stateStyles({
- normal: this.normalStyles,
- focused: this.focusedStyles
- })
- .id(JSON.stringify(item))
- Column() {
- VideoImgRating({ rating: item.getRating() })
- }
+ // Video grid layout.
+ Grid() {
+ ForEach(this.videoImgList, (item: VideoImage, index: number) => {
+ GridItem() {
+ Column() {
+ Stack({ alignContent: Alignment.Center }) {
+ Image(item.getImgSrc())
+ .focusable(true)
+ .groupDefaultFocus(index === 0 ? true : false)
+ .objectFit(ImageFit.Fill)
.width(CommonConstants.FULL_PERCENT)
.height(CommonConstants.FULL_PERCENT)
- .alignItems(HorizontalAlign.End)
- .justifyContent(FlexAlign.End)
+ .draggable(false)
+ .borderRadius($r('app.float.banner_focus_radius'))
+ .id(JSON.stringify(item))
+ Column() {
+ VideoImgRating({ rating: item.getRating() })
}
.width(CommonConstants.FULL_PERCENT)
- // The width and height vary with the container assembly and the aspect ratio remains unchanged.
- .aspectRatio(HomeConstants.VIDEO_DIALOG_ASPECT_RATIO)
- .onHover((isHover: boolean) => {
- if (isHover) {
- this.recommendedCurrentVideo = index;
- } else {
- this.recommendedCurrentVideo = -1;
- }
- })
- .gesture(
- LongPressGesture({ repeat: false })
- .onAction(() => {
- if (index !== 0) {
- Logger.info(`Please long press the first image`);
- return;
- }
- // Obtains all attributes of a component.
- let modePosition: componentUtils.ComponentInfo =
- componentUtils.getRectangleById(JSON.stringify(item));
- let windowOffset = modePosition.windowOffset;
- let size = modePosition.size;
- // Obtains the height of the component from the top.
- let rectTop: number = px2vp(windowOffset.y);
- let rectTop2: number = px2vp(windowOffset.y + Math.floor(size.height));
- // Obtains the width of the component from the left.
- let rectLeft: number = px2vp(windowOffset.x);
- let topHeightNeeded: number = new BreakpointType(HomeConstants.VIDEO_DIALOG_HEIGHTS[0],
+ .height(CommonConstants.FULL_PERCENT)
+ .alignItems(HorizontalAlign.End)
+ .justifyContent(FlexAlign.End)
+ }
+ .focusable(true)
+ .width(CommonConstants.FULL_PERCENT)
+ // The width and height vary with the container assembly and the aspect ratio remains unchanged.
+ .aspectRatio(HomeConstants.VIDEO_DIALOG_ASPECT_RATIO)
+ .hoverEffect(HoverEffect.Scale)
+ .gesture(
+ LongPressGesture({ repeat: false })
+ .onAction(() => {
+ if (index !== 0) {
+ Logger.info(`Please long press the first image`);
+ return;
+ }
+ // Obtains all attributes of a component.
+ let modePosition: componentUtils.ComponentInfo =
+ componentUtils.getRectangleById(JSON.stringify(item));
+ let windowOffset = modePosition.windowOffset;
+ let size = modePosition.size;
+ // Obtains the height of the component from the top.
+ let rectTop: number = px2vp(windowOffset.y);
+ let rectTop2: number = px2vp(windowOffset.y + Math.floor(size.height));
+ // Obtains the width of the component from the left.
+ let rectLeft: number = px2vp(windowOffset.x);
+ let topHeightNeeded: number = new BreakpointType(HomeConstants.VIDEO_DIALOG_HEIGHTS[0],
+ HomeConstants.VIDEO_DIALOG_HEIGHTS[1], HomeConstants.VIDEO_DIALOG_HEIGHTS[2])
+ .getValue(this.currentWidthBreakpoint) + rectTop - rectTop2;
+ if (this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM) {
+ topHeightNeeded += HomeConstants.HOME_HEADER_HEIGHT_SM;
+ }
+ let dialogYOffset: number;
+ // Adaptive pop-up window expansion direction.
+ if (topHeightNeeded < rectTop) {
+ dialogYOffset = rectTop2 - new BreakpointType(HomeConstants.VIDEO_DIALOG_HEIGHTS[0],
HomeConstants.VIDEO_DIALOG_HEIGHTS[1], HomeConstants.VIDEO_DIALOG_HEIGHTS[2])
- .getValue(this.currentWidthBreakpoint) + rectTop - rectTop2;
- if (this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM) {
- topHeightNeeded += HomeConstants.HOME_HEADER_HEIGHT_SM;
- }
- let dialogYOffset: number;
- // Adaptive pop-up window expansion direction.
- if (topHeightNeeded < rectTop) {
- dialogYOffset = rectTop2 - new BreakpointType(HomeConstants.VIDEO_DIALOG_HEIGHTS[0],
- HomeConstants.VIDEO_DIALOG_HEIGHTS[1], HomeConstants.VIDEO_DIALOG_HEIGHTS[2])
- .getValue(this.currentWidthBreakpoint);
- } else {
- dialogYOffset = rectTop;
+ .getValue(this.currentWidthBreakpoint);
+ } else {
+ dialogYOffset = rectTop;
+ }
+ this.windowUtil = WindowUtil.getInstance();
+ let isLayoutFullScreen: boolean = true;
+ let mainWindow = this.windowUtil!.getMainWindow();
+ isLayoutFullScreen = mainWindow!.getWindowProperties().isLayoutFullScreen;
+ // Subtract the width and height of the window in the 2in1 device.
+ if (deviceInfo.deviceType === CommonConstants.DEVICE_TYPE && !isLayoutFullScreen) {
+ dialogYOffset -= HomeConstants.WINDOW_UNDEFINED_TOP;
+ rectLeft -= HomeConstants.WINDOW_UNDEFINED_LEFT;
+ } else {
+ Logger.info(`No need to subtract extra height`);
+ }
+ this.videoDialogController = new CustomDialogController({
+ builder: VideoDialog(),
+ autoCancel: true,
+ customStyle: true,
+ alignment: DialogAlignment.TopStart,
+ offset: {
+ dx: rectLeft,
+ dy: dialogYOffset
}
- this.windowUtil = WindowUtil.getInstance();
- let isLayoutFullScreen: boolean = true;
- let mainWindow = this.windowUtil!.getMainWindow();
- isLayoutFullScreen = mainWindow!.getWindowProperties().isLayoutFullScreen;
- // Subtract the width and height of the window in the 2in1 device.
- if (deviceInfo.deviceType === CommonConstants.DEVICE_TYPE && !isLayoutFullScreen) {
- dialogYOffset -= HomeConstants.WINDOW_UNDEFINED_TOP;
- rectLeft -= HomeConstants.WINDOW_UNDEFINED_LEFT;
- } else {
- Logger.info(`No need to subtract extra height`);
- }
- this.videoDialogController = new CustomDialogController({
- builder: VideoDialog(),
- autoCancel: true,
- customStyle: true,
- alignment: DialogAlignment.TopStart,
- offset: {
- dx: rectLeft,
- dy: dialogYOffset
- }
- });
- // Display the customized pop-up window to play the video.
- this.videoDialogController.open();
- }))
- .bindContextMenu(RightClickMenu(this.currentWidthBreakpoint), ResponseType.RightClick)
+ });
+ // Display the customized pop-up window to play the video.
+ this.videoDialogController.open();
+ }))
+ .bindContextMenu(RightClickMenu(this.currentWidthBreakpoint), ResponseType.RightClick)
- VideoTitle({ title: item.getTitle() })
- VideoContent({ content: item.getContent() })
- }
- .alignItems(HorizontalAlign.Start)
+ VideoTitle({ title: item.getTitle() })
+ VideoContent({ content: item.getContent() })
}
- }, (item: VideoImage, index: number) => index + JSON.stringify(item))
- }
- .tabIndex(getTabIndex(HomeConstants.DIRECTION_LIST[4]))
- .editMode(false)
- // Sets the number of grid layout columns and evenly divides the width.
- .columnsTemplate(this.videoGridColumn)
- .rowsTemplate(CommonConstants.VIDEO_GRID_COLUMNS[0])
- .width(CommonConstants.FULL_PERCENT)
- .rowsGap($r('app.float.video_grid_gap'))
- .columnsGap($r('app.float.video_grid_gap'))
- // Dynamically calculates the total height based on the following numbers of breakpoints.
- .height(this.getGridHeight(this.videoGridColumn, this.currentWidthBreakpoint, this.windowWidth))
- .padding({
- left: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG && this.currentTopIndex === 2 ?
- new BreakpointType($r('app.float.home_recommended_padding_sm'), $r('app.float.home_recommended_padding_md'),
- $r('app.float.home_recommended_padding_lg')).getValue(this.currentWidthBreakpoint) :
- new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
- $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint),
- right: new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
- $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint),
- top: $r('app.float.recommended_video_padding_top')
- })
+ .alignItems(HorizontalAlign.Start)
+ }
+ }, (item: VideoImage, index: number) => index + JSON.stringify(item))
}
+ .tabIndex(getTabIndex(HomeConstants.DIRECTION_LIST[4]))
+ .editMode(false)
+ // Sets the number of grid layout columns and evenly divides the width.
+ .columnsTemplate(this.videoGridColumn)
+ .rowsTemplate(CommonConstants.VIDEO_GRID_COLUMNS[0])
+ .width(CommonConstants.FULL_PERCENT)
+ .rowsGap($r('app.float.video_grid_gap'))
+ .columnsGap($r('app.float.video_grid_gap'))
+ // Dynamically calculates the total height based on the following numbers of breakpoints.
+ .height(this.getGridHeight(this.videoGridColumn, this.currentWidthBreakpoint, this.windowWidth))
+ .padding({
+ left: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG && this.currentTopIndex === 2 ?
+ new BreakpointType($r('app.float.home_recommended_padding_sm'), $r('app.float.home_recommended_padding_md'),
+ $r('app.float.home_recommended_padding_lg')).getValue(this.currentWidthBreakpoint) :
+ new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
+ $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint),
+ right: new BreakpointType($r('app.float.home_content_padding_sm'), $r('app.float.home_content_padding_md'),
+ $r('app.float.home_content_padding_lg')).getValue(this.currentWidthBreakpoint),
+ top: $r('app.float.recommended_video_padding_top'),
+ bottom: $r('app.float.recommended_video_padding_bottom')
+ })
// Zooming and pinching functions of the grid.
.gesture(PinchGesture({ fingers: 2 }).onActionUpdate((event: GestureEvent) => {
if (event.scale > 1 && this.currentWidthBreakpoint !== BreakpointConstants.BREAKPOINT_SM) {
diff --git a/features/home/src/main/resources/base/element/color.json b/features/home/src/main/resources/base/element/color.json
index 26d6c59..1eb948a 100644
--- a/features/home/src/main/resources/base/element/color.json
+++ b/features/home/src/main/resources/base/element/color.json
@@ -51,10 +51,6 @@
{
"name": "tab_background_color",
"value": "#F1F3F5"
- },
- {
- "name": "focus_radius_color",
- "value": "#317AF7"
}
]
}
\ No newline at end of file
diff --git a/features/home/src/main/resources/base/element/float.json b/features/home/src/main/resources/base/element/float.json
index 30e7b95..f7cfb5c 100644
--- a/features/home/src/main/resources/base/element/float.json
+++ b/features/home/src/main/resources/base/element/float.json
@@ -26,11 +26,11 @@
},
{
"name": "banner_height_md",
- "value": "170vp"
+ "value": "194vp"
},
{
"name": "banner_height_lg",
- "value": "218vp"
+ "value": "242vp"
},
{
"name": "banner_text_1_font_sm",
@@ -132,6 +132,10 @@
"name": "swiper_selected_item_width",
"value": "12vp"
},
+ {
+ "name": "indicator_bottom",
+ "value": "12vp"
+ },
{
"name": "new_banner_3_margin",
"value": "6vp"
@@ -148,6 +152,10 @@
"name": "icon_img_size",
"value": "40vp"
},
+ {
+ "name": "icon_img_radius",
+ "value": "20vp"
+ },
{
"name": "icon_img_bottom_margin",
"value": "4vp"
@@ -174,11 +182,7 @@
},
{
"name": "icon_list_column_margin_lg",
- "value": "16vp"
- },
- {
- "name": "icon_list_column_margin",
- "value": "12vp"
+ "value": "4vp"
},
{
"name": "video_grid_gap",
@@ -238,11 +242,11 @@
},
{
"name": "search_tab_font_selected",
- "value": "16fp"
+ "value": "24fp"
},
{
"name": "search_tab_font",
- "value": "14fp"
+ "value": "18fp"
},
{
"name": "main_daily_rating_font_lg",
@@ -542,11 +546,15 @@
},
{
"name": "sub_title_row_height",
- "value": "48vp"
+ "value": "22vp"
},
{
"name": "sub_title_row_margin",
- "value": "24vp"
+ "value": "13vp"
+ },
+ {
+ "name": "focus_more_size",
+ "value": "6vp"
},
{
"name": "video_img_size_lg",
@@ -684,10 +692,6 @@
"name": "history_img_left",
"value": "24vp"
},
- {
- "name": "search_bar_row_padding",
- "value": "24vp"
- },
{
"name": "bottom_tab_bar_width_lg",
"value": "96vp"
@@ -800,6 +804,10 @@
"name": "recommended_video_padding_top",
"value": "12vp"
},
+ {
+ "name": "recommended_video_padding_bottom",
+ "value": "6vp"
+ },
{
"name": "immersive_text_1_font_sm",
"value": "18fp"
@@ -880,25 +888,17 @@
"name": "daily_small_img_radius",
"value": "8vp"
},
- {
- "name": "focus_radius_width",
- "value": "2vp"
- },
{
"name": "text_focus_radius",
- "value": "8vp"
- },
- {
- "name": "input_focus_radius",
- "value": "20vp"
+ "value": "4vp"
},
{
"name": "top_text_width_selected",
- "value": "80vp"
+ "value": "48vp"
},
{
"name": "top_text_width",
- "value": "60vp"
+ "value": "36vp"
},
{
"name": "banner_focus_radius",
@@ -907,10 +907,6 @@
{
"name": "daily_main_focus_radius",
"value": "18vp"
- },
- {
- "name": "icon_focus_radius",
- "value": "24vp"
}
]
}
\ No newline at end of file
diff --git a/features/videoDetail/src/main/ets/view/RelatedList.ets b/features/videoDetail/src/main/ets/view/RelatedList.ets
index db96143..2988ad0 100644
--- a/features/videoDetail/src/main/ets/view/RelatedList.ets
+++ b/features/videoDetail/src/main/ets/view/RelatedList.ets
@@ -199,7 +199,7 @@ export struct RelatedList {
left: $r('app.float.sub_title_row_padding'),
right: $r('app.float.sub_title_row_padding')
})
- .height($r('app.float.sub_title_row_height'))
+ .height($r('app.float.sub_title_row_height_detail'))
.width(CommonConstants.FULL_PERCENT)
this.SubTitle(DetailConstants.SUB_TITLES[4])
@@ -281,7 +281,7 @@ export struct RelatedList {
left: $r('app.float.sub_title_row_padding'),
right: $r('app.float.sub_title_row_padding')
})
- .height($r('app.float.sub_title_row_height'))
+ .height($r('app.float.sub_title_row_height_detail'))
.width(CommonConstants.FULL_PERCENT)
}
@@ -308,7 +308,7 @@ export struct RelatedList {
left: $r('app.float.sub_title_row_padding'),
right: $r('app.float.sub_title_row_padding')
})
- .height($r('app.float.sub_title_row_height'))
+ .height($r('app.float.sub_title_row_height_detail'))
.width(CommonConstants.FULL_PERCENT)
.alignItems(VerticalAlign.Center)
}
diff --git a/features/videoDetail/src/main/ets/view/SelfComment.ets b/features/videoDetail/src/main/ets/view/SelfComment.ets
index 68823b1..e5486af 100644
--- a/features/videoDetail/src/main/ets/view/SelfComment.ets
+++ b/features/videoDetail/src/main/ets/view/SelfComment.ets
@@ -67,6 +67,7 @@ export struct SelfComment {
right: $r('app.float.self_comment_input_right')
})
.width(CommonConstants.FULL_PERCENT)
+ .focusable(false)
}
.alignSelf(ItemAlign.Center)
.layoutWeight(1)
diff --git a/features/videoDetail/src/main/ets/view/VideoDetail.ets b/features/videoDetail/src/main/ets/view/VideoDetail.ets
index dfe47e4..3ad89e5 100644
--- a/features/videoDetail/src/main/ets/view/VideoDetail.ets
+++ b/features/videoDetail/src/main/ets/view/VideoDetail.ets
@@ -13,6 +13,7 @@
* limitations under the License.
*/
+import { KeyCode } from '@kit.InputKit';
import { deviceInfo } from '@kit.BasicServicesKit';
import { display, window } from '@kit.ArkUI';
import { AvPlayerUtil, DeviceScreen, Logger, WindowUtil, BreakpointConstants, CommonConstants, DisplayUtil,
@@ -220,5 +221,22 @@ export struct VideoDetail {
this.isHalfFolded = false;
}
})
+ .onKeyEvent((event?: KeyEvent) => {
+ if (!event || event.type !== KeyType.Down) {
+ return;
+ }
+ if (event.keyCode === KeyCode.KEYCODE_SPACE) {
+ this.avPlayerUtil!.playerStateControl();
+ }
+ if (event.keyCode === KeyCode.KEYCODE_ESCAPE) {
+ this.windowUtil!.recover();
+ }
+ if (event.keyCode === KeyCode.KEYCODE_DPAD_RIGHT) {
+ this.avPlayerUtil!.fastForward();
+ }
+ if (event.keyCode === KeyCode.KEYCODE_DPAD_LEFT) {
+ this.avPlayerUtil!.rewind();
+ }
+ })
}
}
\ No newline at end of file
diff --git a/features/videoDetail/src/main/ets/view/VideoPlayer.ets b/features/videoDetail/src/main/ets/view/VideoPlayer.ets
index d606aa3..0948b00 100644
--- a/features/videoDetail/src/main/ets/view/VideoPlayer.ets
+++ b/features/videoDetail/src/main/ets/view/VideoPlayer.ets
@@ -93,6 +93,15 @@ export struct VideoPlayer {
this.avPlayerUtil?.playerStateControl();
}
})
+ .priorityGesture(
+ TapGesture({ count: 2 })
+ .onAction((event: GestureEvent) => {
+ if (event && deviceInfo.deviceType === CommonConstants.DEVICE_TYPE) {
+ this.isFullScreen = true;
+ this.windowUtil!.maximize();
+ }
+ })
+ )
Column() {
Row() {
@@ -169,6 +178,15 @@ export struct VideoPlayer {
.width(CommonConstants.FULL_PERCENT)
.justifyContent(FlexAlign.End)
.visibility(this.isFullScreen && !this.isShowingSideBar ? Visibility.Visible : Visibility.None)
+ .priorityGesture(
+ TapGesture({ count: 2 })
+ .onAction((event: GestureEvent) => {
+ if (event && deviceInfo.deviceType === CommonConstants.DEVICE_TYPE) {
+ this.windowUtil!.maximize();
+ this.windowUtil!.recover();
+ }
+ })
+ )
Row() {
TimeText({ time: this.currentTime })
@@ -202,9 +220,17 @@ export struct VideoPlayer {
.fillColor(Color.White)
.onClick(() => {
this.isFullScreen = true;
- if (deviceInfo.deviceType !== CommonConstants.DEVICE_TYPE) {
- this.windowUtil!.disableWindowSystemBar();
- }
+ })
+
+ Image($r('app.media.ic_public_fullscreen'))
+ .height($r('app.float.enlarge_size'))
+ .width($r('app.float.enlarge_size'))
+ .margin({ right: $r('app.float.enlarge_margin') })
+ .fillColor(Color.White)
+ .visibility(deviceInfo.deviceType === CommonConstants.DEVICE_TYPE ? Visibility.Visible : Visibility.None)
+ .onClick(() => {
+ this.isFullScreen = true;
+ this.windowUtil!.maximize();
})
}
.width(CommonConstants.FULL_PERCENT)
@@ -216,7 +242,8 @@ export struct VideoPlayer {
.height($r('app.float.back_size'))
.width($r('app.float.back_size'))
.position({
- x: $r('app.float.back_position_x'),
+ x: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG ? $r('app.float.back_position_x') :
+ $r('app.float.back_position_x_lg'),
y: $r('app.float.back_position_y')
})
.fillColor(Color.White)
@@ -236,6 +263,7 @@ export struct VideoPlayer {
.layoutWeight(this.isFullScreen ? 1 : 0)
.width(CommonConstants.FULL_PERCENT)
.backgroundColor(Color.Black)
+ .focusable(false)
}
}
diff --git a/features/videoDetail/src/main/resources/base/element/float.json b/features/videoDetail/src/main/resources/base/element/float.json
index 70ccc26..9f11426 100644
--- a/features/videoDetail/src/main/resources/base/element/float.json
+++ b/features/videoDetail/src/main/resources/base/element/float.json
@@ -125,7 +125,7 @@
"value": "24vp"
},
{
- "name": "sub_title_row_height",
+ "name": "sub_title_row_height_detail",
"value": "48vp"
},
{
@@ -376,6 +376,10 @@
"name": "back_position_x",
"value": "24vp"
},
+ {
+ "name": "back_position_x_lg",
+ "value": "32vp"
+ },
{
"name": "back_position_y",
"value": "36vp"
diff --git a/features/videoDetail/src/main/resources/base/media/ic_public_fullscreen.svg b/features/videoDetail/src/main/resources/base/media/ic_public_fullscreen.svg
new file mode 100644
index 0000000..f9af64b
--- /dev/null
+++ b/features/videoDetail/src/main/resources/base/media/ic_public_fullscreen.svg
@@ -0,0 +1,16 @@
+
--
Gitee
From 9049c647bed1609c9ff19a374d5937f39c5c991d Mon Sep 17 00:00:00 2001
From: lon9 <815882449@qq.com>
Date: Sat, 16 Nov 2024 09:27:32 +0800
Subject: [PATCH 2/6] =?UTF-8?q?=E6=B8=AF=E6=BE=B3=E5=9B=BD=E9=99=85?=
=?UTF-8?q?=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.en.md | 48 +++++++
.../base/src/main/ets/utils/AvPlayerUtil.ets | 132 +++++++++++++-----
.../home/src/main/ets/view/HomeContent.ets | 29 +++-
.../home/src/main/ets/view/HomeHeader.ets | 2 +-
.../src/main/ets/view/VideoDetail.ets | 23 +--
.../src/main/ets/view/VideoDetailView.ets | 17 +--
.../src/main/ets/view/VideoPlayer.ets | 29 ++--
.../main/ets/entryability/EntryAbility.ets | 2 -
screenshots/device/foldable_EN.jpg | Bin 0 -> 79822 bytes
screenshots/device/phone_EN.png | Bin 0 -> 474546 bytes
screenshots/device/tablet_EN.png | Bin 0 -> 2128821 bytes
11 files changed, 208 insertions(+), 74 deletions(-)
create mode 100644 README.en.md
create mode 100644 screenshots/device/foldable_EN.jpg
create mode 100644 screenshots/device/phone_EN.png
create mode 100644 screenshots/device/tablet_EN.png
diff --git a/README.en.md b/README.en.md
new file mode 100644
index 0000000..51101a2
--- /dev/null
+++ b/README.en.md
@@ -0,0 +1,48 @@
+# Long Video
+
+### Introduction
+
+This codelab implements a short video app based on the adaptive layout and responsive layout, achieving one-time development for multi-device deployment. It uses the three-layer project architecture for code reuse and tailors the pages to different device sizes such as mobile phones, foldable phones, tablets, and 2-in-1 devices.
+
+The figure shows the effect on the mobile phone:
+
+
+
+The figure shows the effect on the foldable phone:
+
+
+
+The figure shows the effect on the tablet and 2-in-1 device:
+
+
+
+### Concepts
+
+- One-time development for multi-device deployment: You only need to develop and release one set of project code and then deploy it on multiple devices as demanded. This feature enables you to efficiently develop applications that are compatible with multiple devices while providing distributed user experiences for cross-device transferring, migration, and collaboration.
+- Adaptive layout: When the size of an external container changes, elements can automatically change based on the relative relationship to adapt to the external container. Relative relationships include the proportion, fixed aspect ratio, and display priority.
+- Responsive layout: When the size of an external container changes, elements can automatically change based on the breakpoints, grids, or specific features (such as the screen direction and window width and height) to adapt to the external container.
+- GridRow: A container that is used in a grid layout, together with its child component **GridCol**.
+- GridCol: A container that must be used as a child component of the **GridRow** container.
+- Using the AVPlayer for audio playback: The AVPlayer can be used to play raw media assets in an end-to-end manner.
+
+### Permissions
+
+N/A.
+
+### How to Use
+
+1. Install and open an app on a mobile phone, foldable phone, or tablet. The responsive layout and adaptive layout are used to display different effects on the app pages over different devices.
+2. Swipe up or down the home page content and swipe left or right banners and icon lists.
+3. Touch and hold the first image in the recommended video area to preview the video. Right-click the recommended video area and a menu is displayed. Hover the mouse pointer over the recommended video area to zoom in the image. Pinch two fingers to zoom in or out the video area on a 2-in-1 device or foldable phone.
+4. Switch to the community tab on the top to view the immersive design of the home page. Switch to the video tab on the top to view the innovative layout of the banner images on a 2-in-1 device.
+5. Click the search box on the top to display the search page. Enter "Hua" in the search box and the intelligent prompt page is displayed. Click "Huawei launch event" in the intelligent prompts. The search result page is displayed. Click the Play button in the search result. The video details page is displayed.
+6. By default, the video is played on the video details page. Click the video to play or pause it, and click or drag the progress bar to seek to the playback position.
+7. When you swipe up on a phone or foldable phone, the related list is hidden, the video is zoomed out proportionally, and the comment area is swiped. When you swipe up on a 2-in-1 device, the video is zoomed out proportionally and the introduction area is swiped. Click the full-screen button in the video area to access the full-screen playback page.
+8. Play a video in full-screen mode and click the Play/Pause button to play or pause the video. Click the episode selection button. The episode selection toolbar is displayed.
+
+### Constraints
+
+1. The sample app is supported only on Huawei phones running the standard system.
+2. HarmonyOS: HarmonyOS NEXT Developer Beta 1 or later
+3. DevEco Studio: DevEco Studio NEXT Developer Beta 1 or later
+4. HarmonyOS SDK: HarmonyOS NEXT Developer Beta 1 SDK or later
diff --git a/commons/base/src/main/ets/utils/AvPlayerUtil.ets b/commons/base/src/main/ets/utils/AvPlayerUtil.ets
index 409f2a2..8f16786 100644
--- a/commons/base/src/main/ets/utils/AvPlayerUtil.ets
+++ b/commons/base/src/main/ets/utils/AvPlayerUtil.ets
@@ -24,17 +24,27 @@ export class AvPlayerUtil {
private avPlayer?: media.AVPlayer;
private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
private url: resourceManager.RawFileDescriptor | null = null;
+ private playState: boolean = true;
private surfaceId: string = '';
private sliderBegin: number = 0;
- private isMoving: boolean = false;
+ private startTime: number = 0;
+ private isFullScreen: boolean = false;
private onError: (err: BusinessError) => void = (err: BusinessError) => {
Logger.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
- this.avPlayer?.reset();
+ if (this.avPlayer === undefined) {
+ Logger.error(`AvPlayer is undefined`);
+ return;
+ }
+ this.avPlayer.reset();
}
private onTimeUpdateFunction: (updateTime: number) => void = (updateTime: number) => {
+ if (this.avPlayer === undefined) {
+ Logger.error(`AvPlayer is undefined`);
+ return;
+ }
AppStorage.setOrCreate(CommonConstants.AV_PLAYER_CURRENT_TIME, this.formatTime(updateTime));
AppStorage.setOrCreate(CommonConstants.AV_PLAYER_UPDATE_TIME, updateTime);
- AppStorage.setOrCreate(CommonConstants.AV_PLAYER_PROGRESS, updateTime / this.avPlayer!.duration *
+ AppStorage.setOrCreate(CommonConstants.AV_PLAYER_PROGRESS, updateTime / this.avPlayer.duration *
CommonConstants.PROGRESS_HUNDRED);
}
private onStateChange: (state: media.AVPlayerState) => void = async (state: media.AVPlayerState) => {
@@ -65,20 +75,32 @@ export class AvPlayerUtil {
case CommonConstants.AV_PLAYER_PREPARED_STATE:
this.avPlayer.videoScaleType = media.VideoScaleType.VIDEO_SCALE_TYPE_FIT;
Logger.info('AVPlayer state prepared called.');
+ this.seekToStart();
this.avPlayer.play();
AppStorage.setOrCreate(CommonConstants.AV_PLAYER_TOTAL_TIME, this.formatTime(this.avPlayer.duration));
break;
case CommonConstants.AV_PLAYER_PLAYING_STATE:
Logger.info('AVPlayer state playing called.');
- AppStorage.setOrCreate('avplayerState', this.avPlayer.state);
+ this.playState = true;
+ if (this.isFullScreen) {
+ AppStorage.setOrCreate('fullScreenPlayState', this.playState);
+ }
+ this.seekToStart();
break;
case CommonConstants.AV_PLAYER_PAUSED_STATE:
Logger.info('AVPlayer state paused called.');
- AppStorage.setOrCreate('avplayerState', this.avPlayer.state);
+ this.playState = false;
+ if (this.isFullScreen) {
+ AppStorage.setOrCreate('fullScreenPlayState', this.playState);
+ }
+ this.seekToStart();
break;
case CommonConstants.AV_PLAYER_COMPLETED_STATE:
Logger.info('AVPlayer state completed called.');
- AppStorage.setOrCreate('avplayerState', this.avPlayer.state);
+ this.playState = false;
+ if (this.isFullScreen) {
+ AppStorage.setOrCreate('fullScreenPlayState', this.playState);
+ }
this.avPlayer.stop();
break;
case CommonConstants.AV_PLAYER_STOPPED_STATE:
@@ -96,21 +118,8 @@ export class AvPlayerUtil {
}
}
- static getInstance(): AvPlayerUtil | undefined {
- if (!AppStorage.get('avPlayerUtil')) {
- AppStorage.setOrCreate('avPlayerUtil', new AvPlayerUtil());
- } else {
- Logger.info(`AppStorage does not have avPlayerUtil`);
- }
- return AppStorage.get('avPlayerUtil');
- }
-
- setSurfaceId(surfaceId: string | undefined) {
- this.surfaceId = surfaceId!;
- this.avPlayer!.surfaceId = surfaceId;
- }
-
- async createAvPlayer(surfaceId: string): Promise {
+ async createAvPlayer(surfaceId: string, isFullScreen: boolean): Promise {
+ this.isFullScreen = isFullScreen;
if (this.avPlayer === undefined || this.avPlayer.state === CommonConstants.AV_PLAYER_RELEASE_STATE) {
this.avPlayer = await media.createAVPlayer();
this.surfaceId = surfaceId;
@@ -125,25 +134,54 @@ export class AvPlayerUtil {
}
setAVPlayerCallback(): void {
- this.avPlayer?.on('error', this.onError);
+ if (this.avPlayer === undefined) {
+ Logger.error(`AvPlayer is undefined`);
+ return;
+ }
+ this.avPlayer.on('error', this.onError);
this.onTimeUpdate();
this.setStateChange();
}
onTimeUpdate(): void {
- this.avPlayer?.on('timeUpdate', this.onTimeUpdateFunction);
+ if (this.avPlayer === undefined) {
+ Logger.error(`AvPlayer is undefined`);
+ return;
+ }
+ this.avPlayer.on('timeUpdate', this.onTimeUpdateFunction);
}
offTimeUpdate(): void {
+ if (this.avPlayer === undefined) {
+ Logger.error(`AvPlayer is undefined`);
+ return;
+ }
try {
- this.avPlayer?.off('timeUpdate');
+ this.avPlayer.off('timeUpdate');
} catch (exception) {
Logger.error('Failed to unregister callback. Code: ' + JSON.stringify(exception));
}
}
setStateChange(): void {
- this.avPlayer?.on('stateChange', this.onStateChange);
+ if (this.avPlayer === undefined) {
+ Logger.error(`AvPlayer is undefined`);
+ return;
+ }
+ this.avPlayer.on('stateChange', this.onStateChange)
+ }
+
+ setStartTime(startTime: number): void {
+ this.startTime = startTime;
+ }
+
+ seekToStart(): void {
+ if (this.startTime != 0 && this.avPlayer !== undefined) {
+ this.avPlayer.seek(this.startTime, media.SeekMode.SEEK_PREV_SYNC);
+ this.startTime = 0;
+ } else {
+ Logger.info(`Video is played from the beginning`);
+ }
}
release(): void {
@@ -163,27 +201,27 @@ export class AvPlayerUtil {
sliderChange(value: number, mode: SliderChangeMode): void {
let seekType: media.SeekMode = value > this.sliderBegin ? media.SeekMode.SEEK_PREV_SYNC :
media.SeekMode.SEEK_NEXT_SYNC;
+ if (this.avPlayer === undefined) {
+ Logger.error(`AvPlayer is undefined`);
+ return;
+ }
switch (mode) {
case SliderChangeMode.Begin:
Logger.info(`AvPlayer SliderChangeMode Begin`);
this.sliderBegin = value;
- this.avPlayer?.pause();
+ this.avPlayer.pause();
break;
case SliderChangeMode.Moving:
Logger.info(`AvPlayer SliderChangeMode Moving`);
- this.isMoving = true;
+ this.avPlayer.seek(value / CommonConstants.PROGRESS_HUNDRED * this.avPlayer.duration, seekType);
break;
case SliderChangeMode.End:
Logger.info(`AvPlayer SliderChangeMode End`);
- if (this.isMoving) {
- this.avPlayer?.seek(value / CommonConstants.PROGRESS_HUNDRED * this.avPlayer.duration, seekType);
- this.isMoving = false;
- }
- this.avPlayer?.play();
+ this.avPlayer.play();
break;
case SliderChangeMode.Click:
Logger.info(`AvPlayer SliderChangeMode Click`);
- this.avPlayer?.seek(this.sliderBegin / CommonConstants.PROGRESS_HUNDRED * this.avPlayer.duration, seekType);
+ this.avPlayer.seek(this.sliderBegin / CommonConstants.PROGRESS_HUNDRED * this.avPlayer.duration, seekType);
break;
default:
break;
@@ -191,15 +229,37 @@ export class AvPlayerUtil {
}
playerStateControl(): void {
- if (this.avPlayer?.state === CommonConstants.AV_PLAYER_STOPPED_STATE) {
+ if (this.avPlayer === undefined) {
+ Logger.info(`AvPlayer is undefined`);
+ return;
+ }
+ if (this.avPlayer.state === CommonConstants.AV_PLAYER_STOPPED_STATE) {
this.avPlayer.prepare();
- } else if (this.avPlayer?.state === CommonConstants.AV_PLAYER_PAUSED_STATE) {
+ return;
+ }
+ if (!this.playState) {
this.avPlayer.play();
- } else if (this.avPlayer?.state === CommonConstants.AV_PLAYER_PLAYING_STATE) {
+ } else {
this.avPlayer.pause();
}
}
+ play(): void {
+ if (this.avPlayer !== undefined && !this.playState) {
+ this.avPlayer.play();
+ } else {
+ Logger.info(`AvPlayer play failed`);
+ }
+ }
+
+ pause(): void {
+ if (this.avPlayer !== undefined && this.playState) {
+ this.avPlayer.pause();
+ } else {
+ Logger.info(`AvPlayer pause failed`);
+ }
+ }
+
formatTime(duration: number): string {
let totalSecond: number = Math.round(duration / CommonConstants.PROGRESS_THOUSAND);
let hourNum: number = Math.floor(totalSecond / CommonConstants.SECOND_IN_HOUR);
diff --git a/features/home/src/main/ets/view/HomeContent.ets b/features/home/src/main/ets/view/HomeContent.ets
index c640306..aacd955 100644
--- a/features/home/src/main/ets/view/HomeContent.ets
+++ b/features/home/src/main/ets/view/HomeContent.ets
@@ -13,6 +13,7 @@
* limitations under the License.
*/
+import { window } from '@kit.ArkUI';
import { deviceInfo } from '@kit.BasicServicesKit';
import { BreakpointConstants, BreakpointType, CommonConstants } from '@ohos/commons';
import { Logger, WindowUtil } from '@ohos/commons';
@@ -27,9 +28,14 @@ import { PreviousVideo } from './PreviousVideo';
export struct HomeContent {
@StorageLink('currentWidthBreakpoint') currentWidthBreakpoint: string = BreakpointConstants.BREAKPOINT_LG;
@StorageLink('currentTopIndex') currentTopIndex: number = 0;
+ @StorageLink('windowWidth') windowWidth: number = 0;
@State gridStatus: number = 1;
private windowUtil?: WindowUtil;
+ private mainWindow?: window.Window;
private isSearching: boolean = false;
+ private onWindowSizeChange: (data: window.Size) => void = (data: window.Size) => {
+ this.windowWidth = data.width;
+ }
aboutToAppear(): void {
this.windowUtil = WindowUtil.getInstance();
@@ -40,9 +46,30 @@ export struct HomeContent {
if (deviceInfo.deviceType !== CommonConstants.DEVICE_TYPES[0]) {
this.windowUtil.setFullScreen();
}
+ this.mainWindow = this.windowUtil.getMainWindow();
+ if (this.mainWindow === undefined) {
+ Logger.error(`MainWindow is undefined`);
+ return;
+ }
+ this.windowWidth = this.mainWindow.getWindowProperties().windowRect.width;
+ this.mainWindow.on('windowSizeChange', this.onWindowSizeChange);
}
-
+ aboutToDisappear(): void {
+ this.windowUtil = WindowUtil.getInstance();
+ if (this.windowUtil === undefined) {
+ return;
+ }
+ this.mainWindow = this.windowUtil.getMainWindow();
+ if (this.mainWindow === undefined) {
+ return;
+ }
+ try {
+ this.mainWindow.off('windowSizeChange');
+ } catch (exception) {
+ Logger.error('Failed to unregister the window callback. Code: ' + JSON.stringify(exception));
+ }
+ }
build() {
Column() {
diff --git a/features/home/src/main/ets/view/HomeHeader.ets b/features/home/src/main/ets/view/HomeHeader.ets
index fe929f4..fe6170e 100644
--- a/features/home/src/main/ets/view/HomeHeader.ets
+++ b/features/home/src/main/ets/view/HomeHeader.ets
@@ -147,7 +147,7 @@ export struct HomeHeader {
this.currentTopIndex = index;
this.scrollHeight = 0;
})
- }, (item: string, index: number) => index + JSON.stringify(item))
+ }, (item: number, index: number) => index + JSON.stringify(item))
}
.tabIndex(getTabIndex(HomeConstants.DIRECTION_LIST[1]))
.scrollBar(BarState.Off)
diff --git a/features/videoDetail/src/main/ets/view/VideoDetail.ets b/features/videoDetail/src/main/ets/view/VideoDetail.ets
index d6e9e0e..983ded7 100644
--- a/features/videoDetail/src/main/ets/view/VideoDetail.ets
+++ b/features/videoDetail/src/main/ets/view/VideoDetail.ets
@@ -25,15 +25,15 @@ import { DetailConstants } from '../constants/DetailConstants';
export struct VideoDetail {
@StorageLink('currentWidthBreakpoint') currentWidthBreakpoint: string = BreakpointConstants.BREAKPOINT_LG;
@StorageLink('windowWidth') windowWidth: number = 0;
+ @StorageLink('updateTime') updateTime: number = 0;
@StorageLink('isHalfFolded') isHalfFolded: boolean = false;
- @StorageLink('avplayerState') avplayerState: string = '';
@Consume('pageInfo') pageInfo: NavPathStack;
@State commentImgHeight: string = DetailConstants.INITIAL_COMMENT_IMAGE_HEIGHT;
@State commentImgWidth: string = DetailConstants.INITIAL_COMMENT_IMAGE_WIDTH;
@State relatedVideoHeight: number = DetailConstants.INITIAL_RELATED_VIDEO_HEIGHT;
@State videoHeight: number = DetailConstants.INITIAL_VIDEO_HEIGHT;
@State screenWidth: number = DeviceScreen.getDeviceWidth();
- private avPlayerUtil?: AvPlayerUtil;
+ private avPlayerUtil: AvPlayerUtil= new AvPlayerUtil();
private screenHeight: number = 0;
private windowUtil?: WindowUtil;
private onDetailFoldStatusChange: Callback = (data: display.FoldStatus) => {
@@ -62,7 +62,6 @@ export struct VideoDetail {
};
aboutToAppear() {
- this.avPlayerUtil = AvPlayerUtil.getInstance();
DisplayUtil.getFoldCreaseRegion();
this.screenHeight = DeviceScreen.getDeviceHeight();
this.windowUtil = WindowUtil.getInstance();
@@ -75,15 +74,15 @@ export struct VideoDetail {
}
}
- async aboutToDisappear() {
- this.avPlayerUtil?.offTimeUpdate();
- await this.avPlayerUtil?.release();
+ aboutToDisappear() {
+ this.updateTime = 0;
if (this.windowUtil === undefined) {
return;
}
if (this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_MD && display.isFoldable()) {
this.windowUtil.setMainWindowOrientation(window.Orientation.PORTRAIT);
}
+ this.avPlayerUtil.release();
}
build() {
@@ -141,7 +140,9 @@ export struct VideoDetail {
Column() {
VideoDetailView({
screenHeight: this.screenHeight,
- relatedVideoHeight: $relatedVideoHeight,
+ avPlayerUtil: this.avPlayerUtil,
+ relatedVideoHeight:
+ $relatedVideoHeight,
videoHeight: $videoHeight
})
.layoutWeight(1)
@@ -182,15 +183,15 @@ export struct VideoDetail {
} catch (exception) {
Logger.error('Failed to register callback. Code: ' + JSON.stringify(exception));
}
- if (this.avplayerState !== CommonConstants.AV_PLAYER_PLAYING_STATE) {
- this.avPlayerUtil?.playerStateControl();
- }
if (this.windowUtil === undefined) {
return;
}
if (this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_MD && display.isFoldable()) {
this.isHalfFolded = false;
}
+ this.avPlayerUtil.setStartTime(this.updateTime);
+ this.avPlayerUtil.playerStateControl();
+ this.avPlayerUtil.onTimeUpdate();
})
.onHidden(() => {
try {
@@ -198,6 +199,8 @@ export struct VideoDetail {
} catch (exception) {
Logger.error('Failed to unregister callback. Code: ' + JSON.stringify(exception));
}
+ this.avPlayerUtil.pause();
+ this.avPlayerUtil.offTimeUpdate();
})
}
}
\ No newline at end of file
diff --git a/features/videoDetail/src/main/ets/view/VideoDetailView.ets b/features/videoDetail/src/main/ets/view/VideoDetailView.ets
index fc24a9a..5172d7a 100644
--- a/features/videoDetail/src/main/ets/view/VideoDetailView.ets
+++ b/features/videoDetail/src/main/ets/view/VideoDetailView.ets
@@ -29,14 +29,11 @@ export struct VideoDetailView {
@Consume('pageInfo') pageInfo: NavPathStack;
@Link relatedVideoHeight: number;
@Link videoHeight: number;
- private avPlayerUtil?: AvPlayerUtil;
+ private avPlayerUtil: AvPlayerUtil = new AvPlayerUtil();
private screenHeight: number = 0;
- private scroller: Scroller = new Scroller();
+ private surfaceId: string = '';
private xComponentController: XComponentController = new XComponentController();
-
- aboutToAppear(): void {
- this.avPlayerUtil = AvPlayerUtil.getInstance();
- }
+ private scroller: Scroller = new Scroller();
build() {
Scroll(this.scroller) {
@@ -48,8 +45,8 @@ export struct VideoDetailView {
controller: this.xComponentController
})
.onLoad(() => {
- this.avPlayerUtil?.createAvPlayer(this.xComponentController.getXComponentSurfaceId());
- AppStorage.setOrCreate('detailSurfaceId', this.xComponentController.getXComponentSurfaceId());
+ this.surfaceId = this.xComponentController.getXComponentSurfaceId();
+ this.avPlayerUtil.createAvPlayer(this.surfaceId, false);
})
.width(this.videoHeight + DetailConstants.PERCENT_SIGN)
.height(CommonConstants.FULL_PERCENT)
@@ -69,7 +66,7 @@ export struct VideoDetailView {
value: this.progress
})
.onChange((value: number, mode: SliderChangeMode) => {
- this.avPlayerUtil?.sliderChange(value, mode);
+ this.avPlayerUtil.sliderChange(value, mode);
})
.layoutWeight(1)
.selectedColor($r('app.color.episodes_font'))
@@ -109,7 +106,7 @@ export struct VideoDetailView {
.width(CommonConstants.FULL_PERCENT)
.backgroundColor(Color.Black)
.onClick(() => {
- this.avPlayerUtil?.playerStateControl();
+ this.avPlayerUtil.playerStateControl();
})
RelatedList({
diff --git a/features/videoPlayer/src/main/ets/view/VideoPlayer.ets b/features/videoPlayer/src/main/ets/view/VideoPlayer.ets
index dd473cf..9b48022 100644
--- a/features/videoPlayer/src/main/ets/view/VideoPlayer.ets
+++ b/features/videoPlayer/src/main/ets/view/VideoPlayer.ets
@@ -26,17 +26,19 @@ export struct VideoPlayer {
@StorageLink('currentWidthBreakpoint') currentWidthBreakpoint: string = BreakpointConstants.BREAKPOINT_LG;
@StorageLink('currentHeightBreakpoint') currentHeightBreakpoint: string = BreakpointConstants.BREAKPOINT_LG;
@StorageLink('creaseRegion') creaseRegion: number[] = [];
+ @StorageLink('updateTime') updateTime: number = 0;
@StorageLink('currentTime') currentTime: string = CommonConstants.INITIAL_TIME;
@StorageLink('totalTime') totalTime: string = CommonConstants.INITIAL_TIME;
@StorageLink('progress') progress: number = 0;
- @StorageLink('avplayerState') avplayerState: string = '';
+ @StorageLink('fullScreenPlayState') fullScreenPlayState: boolean = true;
@StorageLink('isHalfFolded') isHalfFolded: boolean = false;
@State isShowingSideBar: boolean = false;
@State foldStatus: display.FoldStatus = display.getFoldStatus();
@Consume('pageInfo') pageInfo: NavPathStack;
private windowUtil?: WindowUtil;
- private avPlayerUtil?: AvPlayerUtil;
+ private avPlayerUtil: AvPlayerUtil = new AvPlayerUtil();
private xComponentController: XComponentController = new XComponentController();
+ private surfaceId: string = '';
private onFoldStatusChange: Callback = (data: display.FoldStatus) => {
this.foldStatus = data;
if (data === display.FoldStatus.FOLD_STATUS_EXPANDED) {
@@ -59,7 +61,6 @@ export struct VideoPlayer {
};
aboutToAppear() {
- this.avPlayerUtil = AvPlayerUtil.getInstance();
this.windowUtil = WindowUtil.getInstance();
if (this.windowUtil !== undefined) {
/**
@@ -86,10 +87,11 @@ export struct VideoPlayer {
} else {
Logger.info(`Full-screen display in portrait mode`);
}
+ this.avPlayerUtil.setStartTime(this.updateTime);
}
- async aboutToDisappear() {
- this.avPlayerUtil?.setSurfaceId(AppStorage.get('detailSurfaceId'));
+ aboutToDisappear() {
+ this.avPlayerUtil.release();
if (this.windowUtil !== undefined) {
/**
* When step out the fullScreen VideoPlay, any device need to show systemBar except 2ini.
@@ -126,7 +128,8 @@ export struct VideoPlayer {
controller: this.xComponentController
})
.onLoad(() => {
- this.avPlayerUtil?.setSurfaceId(this.xComponentController.getXComponentSurfaceId());
+ this.surfaceId = this.xComponentController.getXComponentSurfaceId();
+ this.avPlayerUtil.createAvPlayer(this.surfaceId, true);
})
.aspectRatio(CommonConstants.VIDEO_ASPECT_RATIO)
}
@@ -152,7 +155,7 @@ export struct VideoPlayer {
Slider({ min: 0, max: CommonConstants.PROGRESS_HUNDRED, step: 1, value: this.progress })
.onChange((value: number, mode: SliderChangeMode) => {
- this.avPlayerUtil?.sliderChange(value, mode);
+ this.avPlayerUtil.sliderChange(value, mode);
})
.layoutWeight(1)
.selectedColor($r('app.color.selected_color'))
@@ -169,13 +172,12 @@ export struct VideoPlayer {
Row() {
Row() {
- Image(this.avplayerState === CommonConstants.AV_PLAYER_PLAYING_STATE ? $r('app.media.ic_public_pause')
- : $r('app.media.ic_public_play'))
+ Image(this.fullScreenPlayState ? $r('app.media.ic_public_pause') : $r('app.media.ic_public_play'))
.height($r('app.float.icon_size'))
.width($r('app.float.icon_size'))
.margin({ left: $r('app.float.icon_margin') })
.onClick(() => {
- this.avPlayerUtil?.playerStateControl();
+ this.avPlayerUtil.playerStateControl();
})
ImgIcon({ img: $r('app.media.ic_public_play_next') })
ImgIcon({ img: $r('app.media.ic_public_view_list_white') })
@@ -183,7 +185,7 @@ export struct VideoPlayer {
.margin({
top: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ? '0' : $r('app.float.icon_row_top'),
bottom: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ?
- $r('app.float.icon_row_bottom_sm') : $r('app.float.icon_row_bottom')
+ $r('app.float.icon_row_bottom_sm') : $r('app.float.icon_row_bottom')
})
Blank()
@@ -246,16 +248,15 @@ export struct VideoPlayer {
}
.hideTitleBar(true)
.onShown(() => {
- if (this.avplayerState !== CommonConstants.AV_PLAYER_PLAYING_STATE) {
- this.avPlayerUtil?.playerStateControl();
- }
try {
display.on('foldStatusChange', this.onFoldStatusChange);
} catch (exception) {
Logger.error('Failed to register callback. Code: ' + JSON.stringify(exception));
}
+ this.avPlayerUtil.onTimeUpdate();
})
.onHidden(() => {
+ this.avPlayerUtil.offTimeUpdate();
display.off('foldStatusChange');
})
}
diff --git a/products/phone/src/main/ets/entryability/EntryAbility.ets b/products/phone/src/main/ets/entryability/EntryAbility.ets
index 9e59bc8..14a280f 100644
--- a/products/phone/src/main/ets/entryability/EntryAbility.ets
+++ b/products/phone/src/main/ets/entryability/EntryAbility.ets
@@ -57,11 +57,9 @@ export default class EntryAbility extends UIAbility {
this.windowObj = data;
this.updateWidthBp();
this.updateHeightBp();
- AppStorage.setOrCreate('windowWidth', data.getWindowProperties().windowRect.width);
this.windowObj.on('windowSizeChange', (windowSize: window.Size) => {
this.updateWidthBp();
this.updateHeightBp();
- AppStorage.setOrCreate('windowWidth', windowSize.width);
})
})
diff --git a/screenshots/device/foldable_EN.jpg b/screenshots/device/foldable_EN.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..787eddd04ca34d6b345ac6b1de6a787e28c1e69e
GIT binary patch
literal 79822
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