diff --git a/commons/base/src/main/ets/utils/BreakpointType.ets b/commons/base/src/main/ets/utils/BreakpointType.ets index c36c54dc9e526efdd8b7dade96c8b00769d7361b..95b444b76a9947219c14b2b67d66071620e46fca 100644 --- a/commons/base/src/main/ets/utils/BreakpointType.ets +++ b/commons/base/src/main/ets/utils/BreakpointType.ets @@ -20,6 +20,7 @@ import { BreakpointConstants } from '../constants/BreakpointConstants'; // [Start break_point_type] +// [Start dd_break_point_type] export class BreakpointType { sm: T; md: T; @@ -42,4 +43,5 @@ export class BreakpointType { } } } +// [End dd_break_point_type] // [End break_point_type] \ No newline at end of file diff --git a/commons/base/src/main/ets/utils/WindowUtil.ets b/commons/base/src/main/ets/utils/WindowUtil.ets index ae17fd954ba925b21abaabb1e38ae1bc8cad4f24..88a83d9403d829c4f6885da21b5e39baf17264d7 100644 --- a/commons/base/src/main/ets/utils/WindowUtil.ets +++ b/commons/base/src/main/ets/utils/WindowUtil.ets @@ -141,6 +141,7 @@ export class WindowUtil { } // [Start update_widthbp] + // [Start dd_update_widthbp] updateWidthBp(): void { let mainWindow: window.WindowProperties = this.mainWindowClass!.getWindowProperties(); let windowWidth: number = mainWindow.windowRect.width; @@ -166,9 +167,11 @@ export class WindowUtil { AppStorage.setOrCreate('currentWidthBreakpoint', widthBp); AppStorage.setOrCreate('videoGridColumn', videoGridColumn); } + // [End dd_update_widthbp] // [End update_heightbp] // [Start update_heightbp] + // [Start dd_update_heightbp] updateHeightBp(): void { let mainWindow: window.WindowProperties = this.mainWindowClass!.getWindowProperties(); let windowHeight: number = mainWindow.windowRect.height; @@ -186,5 +189,6 @@ export class WindowUtil { } AppStorage.setOrCreate('currentHeightBreakpoint', heightBp); } + // [End dd_update_heightbp] // [End update_heightbp] } \ No newline at end of file diff --git a/features/home/src/main/ets/view/BannerView.ets b/features/home/src/main/ets/view/BannerView.ets index f1ad4cd7b7273b03efa9359f42d5080496c9dab3..ef24674a87aa03a9ee9d1974aa912b4a167b3e22 100644 --- a/features/home/src/main/ets/view/BannerView.ets +++ b/features/home/src/main/ets/view/BannerView.ets @@ -61,11 +61,14 @@ export struct BannerView { Column() { // Banner layout. Row() { - // [Start swiper1] + // [Start swiper1_lazyforeach] + // [Start jh_lazy_for_each] Swiper() { LazyForEach(this.bannerDataSource, (item: Banner, index: number) => { + // [StartExclude jh_lazy_for_each] Stack() { - // [StartExclude swiper1] + // [StartExclude swiper1_lazyforeach] + // [EndExclude jh_lazy_for_each] Image(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ? item.getBannerImg().getImgSrcSm() : item.getBannerImg().getImgSrc()) .objectFit(ImageFit.Fill) @@ -119,7 +122,7 @@ export struct BannerView { .height('100%') .width('100%') .zIndex(2) - // [EndExclude swiper1] + // [EndExclude swiper1_lazyforeach] } .height(item.getBannerImg().getHeight().getValue(this.currentWidthBreakpoint)) .width('100%') @@ -145,7 +148,7 @@ export struct BannerView { .nextMargin(new BreakpointType($r('app.float.swiper_prev_next_margin_sm'), $r('app.float.swiper_prev_next_margin_md'), $r('app.float.swiper_prev_next_margin_lg')) .getValue(this.currentWidthBreakpoint)) - // [End swiper1] + // [End swiper1_lazyforeach] // Setting the navigation point Style of the swiper. .indicator(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM ? Indicator.dot() .itemWidth($r('app.float.swiper_item_size')) @@ -163,7 +166,8 @@ export struct BannerView { .effectMode(EdgeEffect.None) // The sizes of the front and rear banners on the MD and LG devices are different. .index(2) - .tabIndex(getTabIndex(HomeConstants.DIRECTION_LIST[2])) + .tabIndex(getTabIndex('Banner')) + // [End jh_lazy_for_each] // Switch the tab to switch the banner display effect. // [Start home_constants] diff --git a/features/home/src/main/ets/view/DailyVideo.ets b/features/home/src/main/ets/view/DailyVideo.ets index cd0cd8b68ed24f1093b4e3f8255d341476edf488..76ad521661bbcdae925a16c67044afb1a8ff96a9 100644 --- a/features/home/src/main/ets/view/DailyVideo.ets +++ b/features/home/src/main/ets/view/DailyVideo.ets @@ -117,9 +117,11 @@ export struct DailyVideo { // [End daily_small_video] Column() { + // [Start dd_main_daily_video_img] Text(this.mainDailyVideoImg.getOtherInfo()) .fontSize(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG ? $r('app.float.main_daily_other_font_lg') : $r('app.float.main_daily_other_font')) + // [End dd_main_daily_video_img] .fontWeight(FontWeight.Normal) .maxLines(1) Text(this.mainDailyVideoImg.getContent()) diff --git a/features/home/src/main/ets/view/Home.ets b/features/home/src/main/ets/view/Home.ets index 59e747081ec4a61db854fa13e76004d1d9d4a447..0e98c3dbaf453fe9c6c72aa9d93c3611cce19188 100644 --- a/features/home/src/main/ets/view/Home.ets +++ b/features/home/src/main/ets/view/Home.ets @@ -59,10 +59,11 @@ export struct Home { } build() { + // [Start tabs_barposition] Tabs({ barPosition: this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG ? BarPosition.Start : BarPosition.End }) { - + // [StartExclude tabs_barposition] // [Start tab_content] TabContent() { if (this.currentTopIndex === 2) { @@ -134,6 +135,7 @@ export struct Home { Column() } .tabBar(this.BottomTabBuilder(this.tabList[3], 3)) + // [EndExclude tabs_barposition] } // Change the position and size of the tab at the bottom. .barWidth(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG ? $r('app.float.bottom_tab_bar_width_lg') : @@ -144,6 +146,7 @@ export struct Home { .barMode(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG ? BarMode.Scrollable : BarMode.Fixed, { nonScrollableLayoutStyle: LayoutStyle.ALWAYS_CENTER }) .vertical(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG) + // [End tabs_barposition] .barOverlap(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG && this.currentTopIndex === 2 ? true : false) .barBackgroundBlurStyle(BlurStyle.NONE) diff --git a/features/home/src/main/ets/view/HomeContent.ets b/features/home/src/main/ets/view/HomeContent.ets index eb1cef79a4d6ac459989cb0e232ffd510be4e77e..d338b0f256610bc4f41d70b7ba90a1c4924d67ee 100644 --- a/features/home/src/main/ets/view/HomeContent.ets +++ b/features/home/src/main/ets/view/HomeContent.ets @@ -39,15 +39,16 @@ export struct HomeContent { } build() { + // [Start banner_view] Column() { - // [Start banner_view] + // [EndExclude banner_view] BannerView() IconView() RecommendedVideo() NewVideoRelease() DailyVideo() PreviousVideo() - // [End banner_view] + // [EndExclude banner_view] } // Set the background image to cover the side and top tabs. .backgroundImage(this.currentTopIndex === 2 && !this.isSearching ? new BreakpointType( @@ -61,6 +62,7 @@ export struct HomeContent { .backgroundColor(this.currentTopIndex === 2 && !this.isSearching ? (this.currentWidthBreakpoint !== BreakpointConstants.BREAKPOINT_MD ? $r('app.color.home_content_background') : $r('app.color.home_content_background_md')) : Color.White) .width(CommonConstants.FULL_PERCENT) + // [End banner_view] .padding({ bottom: deviceInfo.deviceType !== CommonConstants.DEVICE_TYPE && this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG ? $r('app.float.bottom_navigation') : 0 }) } diff --git a/features/home/src/main/ets/view/HomeHeader.ets b/features/home/src/main/ets/view/HomeHeader.ets index 0a19c06446c29791f9cd3cc53c81519983d6dc04..8894cfb8ab396d7c2610bc03b811f6b5b44818fb 100644 --- a/features/home/src/main/ets/view/HomeHeader.ets +++ b/features/home/src/main/ets/view/HomeHeader.ets @@ -32,6 +32,7 @@ export struct HomeHeader { @StorageLink('currentTopIndex') currentTopIndex: number = 0; // [Start Home_Header] + // [Start build_column] build() { Column() { GridRow({ @@ -78,6 +79,7 @@ export struct HomeHeader { } .width('100%') } + // [End build_column] // [End Home_Header] // [Start top_tab_bar] diff --git a/features/home/src/main/ets/view/RecommendedVideo.ets b/features/home/src/main/ets/view/RecommendedVideo.ets index fbccb2e4dd92dc59ec9ad740e162a19fe30f90d7..9a7956b91575d221eb9075758fc25090f3edd26b 100644 --- a/features/home/src/main/ets/view/RecommendedVideo.ets +++ b/features/home/src/main/ets/view/RecommendedVideo.ets @@ -42,7 +42,9 @@ export struct RecommendedVideo { build() { // Video grid layout. // [Start grid_foreach] + // [Start jh_tabindex_gettabindex] Grid() { + // [StartExclude jh_tabindex_gettabindex] ForEach(this.videoImgList, (item: VideoImage, index: number) => { GridItem() { Column() { @@ -50,12 +52,16 @@ export struct RecommendedVideo { Stack({ alignContent: Alignment.Center }) { // [StartExclude aligncontent] // [StartExclude build_grid] + + // [Start jh_get_img_src] Image(item.getImgSrc()) - .focusable(true) - .groupDefaultFocus(index === 0 ? true : false) .objectFit(ImageFit.Fill) .width('100%') .height('100%') + .hoverEffect(HoverEffect.Scale) + // [End jh_get_img_src] + .focusable(true) + .groupDefaultFocus(index === 0 ? true : false) .draggable(false) .borderRadius($r('app.float.banner_focus_radius')) .id(JSON.stringify(item)) @@ -142,6 +148,7 @@ export struct RecommendedVideo { .alignItems(HorizontalAlign.Start) } }, (item: VideoImage, index: number) => index + JSON.stringify(item)) + // [EndExclude jh_tabindex_gettabindex] } // [StartExclude build_grid] // [StartExclude grid_foreach] @@ -203,6 +210,7 @@ export struct RecommendedVideo { Logger.info(`Two-finger operation is not supported`); } })) + // [End jh_tabindex_gettabindex] // [End grid_foreach] } diff --git a/features/home/src/main/ets/view/VideoDaily.ets b/features/home/src/main/ets/view/VideoDaily.ets new file mode 100644 index 0000000000000000000000000000000000000000..67784760e031db97d5e19aa60ab6185c6f16c6d7 --- /dev/null +++ b/features/home/src/main/ets/view/VideoDaily.ets @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AvPlayerUtil, BreakpointConstants, CommonConstants, WindowUtil } from '@ohos/commons'; +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, window } from '@kit.ArkUI'; +import { deviceInfo } from '@kit.BasicServicesKit'; +import { KeyCode } from '@kit.InputKit'; + +@Component +export struct DailyVideo { + @State scrollHeight: number =0; + @StorageLink('currentWidthBreakpoint') currentWidthBreakpoint: string = 'lg'; + @StorageLink('currentHeightBreakpoint') currentHeightBreakpoint: string = 'lg'; + @StorageLink('windowWidth') windowWidth: number = 0; + @StorageLink('currentTopIndex') currentTopIndex: number = 0; + @State isShowingMainBorder: boolean = false; + private mainWindowClass?: window.Window; + @StorageLink('isFullScreen') isFullScreen: boolean = false; + private windowUtil?: WindowUtil = WindowUtil.getInstance(); + @StorageLink('isHalfFolded') isHalfFolded: boolean = false; + private avPlayerUtil?: AvPlayerUtil; + private xComponentController: XComponentController = new XComponentController(); + + aboutToAppear(): void { + // [Start dd_judgment_of] + // Judgment of the horizontal window. (The actual application may need to be combined with other conditions, for example, determine the horizontal breakpoint) + if (this.currentHeightBreakpoint === 'sm' && this.currentWidthBreakpoint === 'md') { + // Horizontal window page layout. + } + // Judgment of the square window. (The actual use may need to be combined with other conditions, such as determining horizontal breakpoints) + if (this.currentHeightBreakpoint === 'md' && this.currentWidthBreakpoint === 'sm') { + // Square-like window page layout. + } + // [End dd_judgment_of] + + // [Start dd_set_preferred_orientation] + let currentHeightBreakpoint: string | undefined = AppStorage.get('currentHeightBreakpoint'); + if (currentHeightBreakpoint === 'md') { + this.mainWindowClass?.setPreferredOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED); + } + // [End dd_set_preferred_orientation] + + // [Start dd_windowrect_rect] + let windowRect: window.Rect = this.mainWindowClass!.getWindowProperties().windowRect; + let windowWidthVp: number = px2vp(windowRect.width); + let windowHeightVp: number = px2vp(windowRect.height); + let aspectRatio: number = windowHeightVp / windowWidthVp; + if (aspectRatio < 1.2 && aspectRatio >= 0.8) { + this.mainWindowClass?.setPreferredOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED); + } + // [Start dd_windowrect_rect] + + // [Start dd_isfullscreen] + if (this.isFullScreen) { + if (deviceInfo.deviceType !== '2in1') { + this.windowUtil!.disableWindowSystemBar(); + } + if ((!display.isFoldable() && deviceInfo.deviceType === 'phone') || + display.getFoldStatus() === display.FoldStatus.FOLD_STATUS_FOLDED) { + this.windowUtil!.setMainWindowOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE); + } + if (display.isFoldable()) { + if (this.isHalfFolded) { + this.windowUtil!.setMainWindowOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE); + } + } + } + // [End dd_isfullscreen] + } + build() { + Column(){ + // [Start dd_break_point_type_getValue] + Text('Test') + .fontSize(new BreakpointType('14fp', '16fp', '18fp').getValue(this.currentWidthBreakpoint)) + // [End dd_break_point_type_getValue] + + // [Start dd_movable_layout] + // Movable layout. + GridRow({ + columns: { sm: 4, md: 12, lg: 12 }, + gutter: 12, + breakpoints: { value: ['320vp', '600vp', '840vp'], reference: BreakpointsReference.WindowSize }, + direction: GridRowDirection.Row + }) { + GridCol({ + span: { sm: 4, md: 7, lg: 7 } + }) { + this.topTabBar() + } + .height(56) + + GridCol({ + span: { sm: 4, md: 5, lg: 5 } + }) { + this.topSearch() + } + .height(56) + } + .width('100%') + // [End dd_movable_layout] + + // [Start dd_carousel_layout] + // Carousel layout. + Swiper() { + } + .displayCount(new BreakpointType(1, 2, 3).getValue(this.currentWidthBreakpoint)) + .nextMargin(new BreakpointType(0, 16, 32).getValue(this.currentWidthBreakpoint)) + .prevMargin(new BreakpointType(0, 16, 32).getValue(this.currentWidthBreakpoint)) + .indicator(this.currentWidthBreakpoint === 'sm' ? Indicator.dot() + .itemWidth(6) + .itemHeight(6) + .selectedItemWidth(12) + .selectedItemHeight(6) + .color('#4DFFFFFF') + .bottom(6) + .selectedColor(Color.White) : false + ) + .padding({ + left: this.currentWidthBreakpoint === 'sm' ? 12 : 0, + right: this.currentWidthBreakpoint === 'sm' ? 12 : 0 + }) + // [End dd_carousel_layout] + + // [Start dd_palace_grid_layout] + // Palace grid layout. + Grid() { + } + .columnsTemplate('1fr '.repeat(this.currentWidthBreakpoint === 'sm' ? 4 : 8)) + .rowsTemplate(this.currentWidthBreakpoint === 'sm' ? '1fr 1fr' : '1fr') + .height(this.currentWidthBreakpoint === 'sm' ? 164 : 78) + .columnsGap(12) + .rowsGap(12) + // [End dd_palace_grid_layout] + + // [Start jh_xcomponent] + XComponent({ id: '', type: 'surface', controller: this.xComponentController }) + .onKeyEvent((event?: KeyEvent) => { + // If the button type is pressed, the subsequent code will not be executed; the specific button logic will be executed upon release. + if (!event || event.type !== KeyType.Down) { + return; + } + // Space key controls pause/play + if (event.keyCode === KeyCode.KEYCODE_SPACE) { + this.avPlayerUtil!.playerStateControl(); + } + // ESC key exits full screen + if (event.keyCode === KeyCode.KEYCODE_ESCAPE) { + this.windowUtil!.recover(); + } + // Right-click to fast forward + if (event.keyCode === KeyCode.KEYCODE_DPAD_RIGHT) { + this.avPlayerUtil!.fastForward(); + } + // Left-click to rewind + if (event.keyCode === KeyCode.KEYCODE_DPAD_LEFT) { + this.avPlayerUtil!.rewind(); + } + }) + // [End jh_xcomponent] + } + } + + @Builder + topTabBar() { + Row() { + Column() { + List() { + ForEach(HomeConstants.TOP_TOPIC_LIST, (item: string, index: number) => { + ListItem() { + Text(item) + .fontSize(this.currentTopIndex === index ? $r('app.float.search_tab_font_selected') : + $r('app.float.search_tab_font')) + .fontWeight(this.currentTopIndex === index ? 700 : + 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; + }) + }, (item: number, index: number) => index + JSON.stringify(item)) + } + .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) + 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'), + right: $r('app.float.top_tab_img_margin') + }) + .fillColor(this.currentTopIndex === 2 ? Color.White : Color.Black) + .visibility(this.currentWidthBreakpoint === 'sm' ? Visibility.Visible : Visibility.None) + } + .height($r('app.float.top_bar_height')) + .width('100%') + } + + @Builder + topSearch() { + Row() { + Column() { + List() { + ForEach(HomeConstants.TOP_TOPIC_LIST, (item: string, index: number) => { + ListItem() { + Text(item) + .fontSize(this.currentTopIndex === index ? $r('app.float.search_tab_font_selected') : + $r('app.float.search_tab_font')) + .fontWeight(this.currentTopIndex === index ? 700 : + 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; + }) + }, (item: number, index: number) => index + JSON.stringify(item)) + } + .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) + 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'), + right: $r('app.float.top_tab_img_margin') + }) + .fillColor(this.currentTopIndex === 2 ? Color.White : Color.Black) + .visibility(this.currentWidthBreakpoint === 'sm' ? Visibility.Visible : Visibility.None) + } + .height($r('app.float.top_bar_height')) + .width('100%') + } +} \ No newline at end of file diff --git a/features/videoDetail/src/main/ets/view/VideoDetailChange.ets b/features/videoDetail/src/main/ets/view/VideoDetailChange.ets new file mode 100644 index 0000000000000000000000000000000000000000..d0a0a31af2a092a42b6d39ff418cf66e8a5474c8 --- /dev/null +++ b/features/videoDetail/src/main/ets/view/VideoDetailChange.ets @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * 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, + VideoNavPathStack +} from '@ohos/commons'; +import { SelfComment } from './SelfComment'; +import { AllComments } from './AllComments'; +import { VideoDetailView } from './VideoDetailView'; +import { DetailConstants } from '../constants/DetailConstants'; + +@Component +export struct VideoDetail { + @StorageLink('currentWidthBreakpoint') currentWidthBreakpoint: string = BreakpointConstants.BREAKPOINT_LG; + @StorageLink('currentHeightBreakpoint') currentHeightBreakpoint: string = BreakpointConstants.BREAKPOINT_LG; + @StorageLink('windowWidth') windowWidth: number = 0; + @StorageLink('isHalfFolded') @Watch('onHalfFoldedChange') isHalfFolded: boolean = false; + @StorageLink('avplayerState') avplayerState: string = ''; + @StorageLink('isFullScreen') @Watch('onFullScreenChange') isFullScreen: boolean = false; + @Consume('pageInfo') pageInfo: VideoNavPathStack; + @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; + private avPlayerUtil?: AvPlayerUtil = AvPlayerUtil.getInstance(); + public screenHeight: number = 0; + private windowUtil?: WindowUtil = WindowUtil.getInstance(); + private mainWindow?: window.Window; + private onWindowSizeChange: (windowSize: window.Size) => void = (windowSize: window.Size) => { + if (this.pageInfo.getPageName() !== CommonConstants.PAGE_NAMES[1]) { + return; + } + if (((this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_MD && this.currentHeightBreakpoint !== + BreakpointConstants.BREAKPOINT_SM) || this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG) && + !this.isHalfFolded) { + this.windowUtil?.setMainWindowOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED); + } + else if (this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_MD && this.currentHeightBreakpoint === + BreakpointConstants.BREAKPOINT_SM) { + if (this.isFullScreen) { + this.windowUtil?.setMainWindowOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE_RESTRICTED); + } else { + this.windowUtil?.setMainWindowOrientation(window.Orientation.PORTRAIT); + } + } else if (this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM && this.currentHeightBreakpoint === + BreakpointConstants.BREAKPOINT_LG) { + if (this.isFullScreen) { + this.windowUtil?.setMainWindowOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE_RESTRICTED); + } else { + this.windowUtil?.setMainWindowOrientation(window.Orientation.PORTRAIT); + } + } + }; + + private onHalfFoldedChange(): void { + if (this.isHalfFolded) { + this.windowUtil?.setMainWindowOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE_RESTRICTED); + } else { + if (this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_MD && this.currentHeightBreakpoint === + BreakpointConstants.BREAKPOINT_MD) { + this.windowUtil?.setMainWindowOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED); + } + } + } + + // [Start on_full_screen_change] + // [Start dd_on_full_screen_change] + // features/videoDetail/src/main/ets/view/VideoDetail.ets + private onFullScreenChange(): void { + // Large folding screen (X series) in unfolded state and tablet state, supporting rotation && large folding screen (X series) in hover state requires landscape display and does not support rotation. + if (((this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_MD && this.currentHeightBreakpoint !== + BreakpointConstants.BREAKPOINT_SM) || this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG) && + !this.isHalfFolded) { + this.windowUtil?.setMainWindowOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED); + } + // Phone and large folding screen (X series) in portrait mode. + else if (this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_SM && this.currentHeightBreakpoint === + BreakpointConstants.BREAKPOINT_LG) { + // In full-screen mode, the layout is displayed in landscape mode. Otherwise, the layout is displayed in portrait mode. + if (this.isFullScreen) { + this.windowUtil?.setMainWindowOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE_RESTRICTED); + } else { + this.windowUtil?.setMainWindowOrientation(window.Orientation.PORTRAIT); + } + } + // When the mobile phone and large folding screen (X series) are folded in landscape mode and the playback is not in full screen mode, the vertical display layout is displayed. + else if (this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_MD && this.currentHeightBreakpoint === + BreakpointConstants.BREAKPOINT_SM && !this.isFullScreen) { + this.windowUtil?.setMainWindowOrientation(window.Orientation.PORTRAIT); + } + // The navigation bar is not hidden on a 2in1 device. + if (deviceInfo.deviceType !== '2in1') { + // The navigation bar is hidden in full-screen playback. Otherwise, the navigation bar is displayed. + if (this.isFullScreen) { + this.windowUtil!.disableWindowSystemBar(); + } else { + this.windowUtil!.enableWindowSystemBar(); + } + } + } + // [End dd_on_full_screen_change] + // [End on_full_screen_change] + + aboutToAppear() { + DisplayUtil.getFoldCreaseRegion(); + this.screenHeight = DeviceScreen.getDeviceHeight(); + this.mainWindow = this.windowUtil!.getMainWindow(); + this.mainWindow?.on('windowSizeChange', this.onWindowSizeChange); + if (this.currentWidthBreakpoint !== BreakpointConstants.BREAKPOINT_SM) { + this.windowUtil!.setMainWindowOrientation(window.Orientation.AUTO_ROTATION_RESTRICTED); + } + } + + async aboutToDisappear() { + this.isFullScreen = false; + this.avPlayerUtil?.offTimeUpdate(); + await this.avPlayerUtil?.release(); + + if (this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG) { + this.windowUtil!.setMainWindowOrientation(window.Orientation.LANDSCAPE); + } else { + this.windowUtil!.setMainWindowOrientation(window.Orientation.PORTRAIT); + } + } + + build() { + NavDestination() { + GridRow({ + columns: { + sm: BreakpointConstants.GRID_ROW_COLUMNS[2], + md: BreakpointConstants.GRID_ROW_COLUMNS[0], + lg: BreakpointConstants.GRID_ROW_COLUMNS[0] + } + }) { + GridCol({ + span: { + sm: BreakpointConstants.GRID_COLUMN_SPANS[5], + md: BreakpointConstants.GRID_COLUMN_SPANS[0], + lg: BreakpointConstants.GRID_COLUMN_SPANS[0] + } + }) { + SideBarContainer() { + Column() { + // Sidebar area. + Scroll() { + AllComments({ commentImgHeight: $commentImgHeight, commentImgWidth: $commentImgWidth }) + .visibility(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG ? Visibility.Visible : + Visibility.None) + } + .align(Alignment.Top) + .scrollBar(BarState.Off) + .layoutWeight(1) + .width(CommonConstants.FULL_PERCENT) + .padding({ bottom: $r('app.float.side_scroll_padding') }) + + SelfComment() + .visibility(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG ? Visibility.Visible : + Visibility.None) + } + .justifyContent(FlexAlign.Start) + .height(CommonConstants.FULL_PERCENT) + .width(CommonConstants.FULL_PERCENT) + .backgroundColor(Color.White) + .onAreaChange((newValue: Area) => { + if (newValue.width !== 0) { + // Handling when the width of the sidebar changes. + let height: number = DetailConstants.COMMENT_IMAGE_MIN_HEIGHT_NUMBER + (Number(newValue.width) - + DetailConstants.SIDE_BAR_MIN_WIDTH_NUMBER) / + (px2vp(this.windowWidth) * DetailConstants.COMMENTS_AREA_PERCENT - + DetailConstants.SIDE_BAR_MIN_WIDTH_NUMBER) * (DetailConstants.COMMENT_IMAGE_MAX_HEIGHT_NUMBER - + DetailConstants.COMMENT_IMAGE_MIN_HEIGHT_NUMBER); + let width: number = DetailConstants.COMMENT_IMAGE_MIN_WIDTH_NUMBER + (Number(newValue.width) - + DetailConstants.SIDE_BAR_MIN_WIDTH_NUMBER) / + (px2vp(this.windowWidth) * DetailConstants.COMMENTS_AREA_PERCENT - + DetailConstants.SIDE_BAR_MIN_WIDTH_NUMBER) * (DetailConstants.COMMENT_IMAGE_MAX_WIDTH_NUMBER - + DetailConstants.COMMENT_IMAGE_MIN_WIDTH_NUMBER); + this.commentImgHeight = JSON.stringify(height); + this.commentImgWidth = JSON.stringify(width); + } + }) + + Column() { + // Content area. + VideoDetailView({ + screenHeight: this.screenHeight, + relatedVideoHeight: this.relatedVideoHeight, + videoHeight: this.videoHeight + }) + .layoutWeight(1) + SelfComment() + .visibility(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG || this.isFullScreen ? + Visibility.None : Visibility.Visible) + } + .height(CommonConstants.FULL_PERCENT) + .width(CommonConstants.FULL_PERCENT) + } + .showSideBar(this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_LG && !this.isFullScreen ? true : + false) + .showControlButton(false) + .autoHide(false) + .sideBarPosition(SideBarPosition.End) + .sideBarWidth($r('app.float.side_bar_min_width')) + .minSideBarWidth($r('app.float.side_bar_min_width')) + .maxSideBarWidth(px2vp(this.windowWidth * DetailConstants.COMMENTS_AREA_PERCENT)) + } + .height(CommonConstants.FULL_PERCENT) + } + .width(CommonConstants.FULL_PERCENT) + .height(CommonConstants.FULL_PERCENT) + .onBreakpointChange((breakPoints) => { + if (breakPoints !== BreakpointConstants.BREAKPOINT_LG && + this.videoHeight < DetailConstants.INITIAL_VIDEO_HEIGHT) { + this.relatedVideoHeight = 0; + } else if (breakPoints === BreakpointConstants.BREAKPOINT_LG) { + this.relatedVideoHeight = DetailConstants.INITIAL_RELATED_VIDEO_HEIGHT; + } else { + Logger.info(`No specific function`); + } + }) + } + .hideTitleBar(true) + .onShown(() => { + if (this.avplayerState !== CommonConstants.AV_PLAYER_PLAYING_STATE) { + this.avPlayerUtil!.playerStateControl(); + } + if (canIUse('SystemCapability.Window.SessionManager')) { + if (this.currentWidthBreakpoint === BreakpointConstants.BREAKPOINT_MD && display.isFoldable()) { + this.isHalfFolded = false; + } + } + }) + .onKeyEvent((event?: KeyEvent) => { + //If the key type is pressed, the subsequent code will not be executed, and the specific key logic will be executed when released. + if (!event || event.type !== KeyType.Down) { + return; + } + // Space key controls pause/play. + if (event.keyCode === KeyCode.KEYCODE_SPACE) { + this.avPlayerUtil!.playerStateControl(); + } + //press ESC to exit full screen. + if (event.keyCode === KeyCode.KEYCODE_ESCAPE) { + this.windowUtil!.recover(); + } + //Right-click fast forward + if (event.keyCode === KeyCode.KEYCODE_DPAD_RIGHT) { + this.avPlayerUtil!.fastForward(); + } + //Left click to go back quickly + 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 9992b36fb20973290e5d6b590320da382095417c..74942f95d2174927df802b5a922a36ea5dd5d559 100644 --- a/features/videoDetail/src/main/ets/view/VideoPlayer.ets +++ b/features/videoDetail/src/main/ets/view/VideoPlayer.ets @@ -69,15 +69,19 @@ export struct VideoPlayer { build() { // [Start stack_aligncontent] + // [Start stack_isfullscreen] Stack({ alignContent: this.isFullScreen ? Alignment.Center : Alignment.Bottom }) { Flex({ + // [StartExclude stack_isfullscreen] // [StartExclude stack_aligncontent] direction: FlexDirection.Column, justifyContent: this.isHalfFolded ? FlexAlign.Start : FlexAlign.Center, alignItems: ItemAlign.Start + // [EndExclude stack_isfullscreen] // [EndExclude stack_aligncontent] }) { Column() { + // [StartExclude stack_isfullscreen] // [StartExclude stack_aligncontent] XComponent({ id: CommonConstants.PAGE_NAMES[1], @@ -91,11 +95,13 @@ export struct VideoPlayer { .width(this.isFullScreen ? -1 : this.videoHeight + DetailConstants.PERCENT_SIGN) .aspectRatio(CommonConstants.VIDEO_ASPECT_RATIO) // [EndExclude stack_aligncontent] + // [EndExclude stack_isfullscreen] } .justifyContent(FlexAlign.Center) .height(this.isHalfFolded ? this.creaseRegion[0] : (this.isFullScreen ? CommonConstants.FULL_PERCENT : 'auto')) .width(CommonConstants.FULL_PERCENT) // [StartExclude stack_aligncontent] + // [StartExclude stack_isfullscreen] } .width(CommonConstants.FULL_PERCENT) .onClick(() => { @@ -115,7 +121,9 @@ export struct VideoPlayer { }) ) // [EndExclude stack_aligncontent] + // [EndExclude stack_isfullscreen] Column() { + // [StartExclude stack_isfullscreen] // [StartExclude stack_aligncontent] Row() { TimeText({ time: this.currentTime }) @@ -187,6 +195,7 @@ export struct VideoPlayer { .width(CommonConstants.FULL_PERCENT) .visibility(this.isFullScreen ? Visibility.Visible : Visibility.None) // [EndExclude stack_aligncontent] + // [EndExclude stack_isfullscreen] } .height(this.isFullScreen ? CommonConstants.FULL_PERCENT : 'auto') .width(CommonConstants.FULL_PERCENT) @@ -201,6 +210,7 @@ export struct VideoPlayer { } }) ) + // [StartExclude stack_isfullscreen] // [StartExclude stack_aligncontent] Row() { TimeText({ time: this.currentTime }) @@ -253,6 +263,7 @@ export struct VideoPlayer { .visibility(!this.isFullScreen ? Visibility.Visible : Visibility.None) // [EndExclude stack_aligncontent] + // [EndExclude stack_isfullscreen] Image($r('app.media.ic_public_back')) .height($r('app.float.back_size')) .width($r('app.float.back_size')) @@ -280,6 +291,7 @@ export struct VideoPlayer { .backgroundColor(Color.Black) .focusable(false) } + // [End stack_isfullscreen] // [End stack_aligncontent] } diff --git a/products/phone/src/main/ets/entryability/EntryAbility.ets b/products/phone/src/main/ets/entryability/EntryAbility.ets index d30f5e19b4cca4ae81e7bdc2174ceda8ffb619bc..6b48177944e5773fab929d2dcccde8f1bfdbfd21 100644 --- a/products/phone/src/main/ets/entryability/EntryAbility.ets +++ b/products/phone/src/main/ets/entryability/EntryAbility.ets @@ -62,7 +62,7 @@ export default class EntryAbility extends UIAbility { AppStorage.setOrCreate('windowWidth', data.getWindowProperties().windowRect.width); this.windowObj.on('windowSizeChange', this.onWindowSizeChange); }); - // [StartExclude EntryAbility] + // [c EntryAbility] windowStage.loadContent('pages/Index', (err, data) => { AppStorage.setOrCreate('uiContext', windowStage.getMainWindowSync().getUIContext()); if (err.code) { diff --git a/products/phone/src/main/ets/entryability/EntryAbilityChange.ets b/products/phone/src/main/ets/entryability/EntryAbilityChange.ets new file mode 100644 index 0000000000000000000000000000000000000000..ef5b61e293eeb625796109b026852ab8fe349eda --- /dev/null +++ b/products/phone/src/main/ets/entryability/EntryAbilityChange.ets @@ -0,0 +1,33 @@ +import { hilog } from "@kit.PerformanceAnalysisKit"; +import { BusinessError } from "@kit.BasicServicesKit"; +import { window } from "@kit.ArkUI"; +import { WindowUtil } from "@ohos/commons"; +import { UIAbility } from "@kit.AbilityKit"; + +// [Start dd_entryability_uicontext] +// [Start dd_entryability_default] +export default class EntryAbility extends UIAbility { + private windowUtil?: WindowUtil = WindowUtil.getInstance(); + private onWindowSizeChange: (windowSize: window.Size) => void = (windowSize: window.Size) => { + this.windowUtil!.updateHeightBp(); + this.windowUtil!.updateWidthBp(); + AppStorage.setOrCreate('windowWidth', windowSize.width); + }; + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + this.windowUtil?.setWindowStage(windowStage); + + windowStage.getMainWindow((err: BusinessError, data: window.Window) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to get the main window. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + this.windowUtil!.updateWidthBp(); + this.windowUtil!.updateHeightBp(); + data.on('windowSizeChange', this.onWindowSizeChange); + }) + } +} +// [End dd_entryability_default] +// [End dd_entryability_uicontext] \ No newline at end of file diff --git a/products/phone/src/main/ets/entryability/EntryAbilityNew.ets b/products/phone/src/main/ets/entryability/EntryAbilityNew.ets new file mode 100644 index 0000000000000000000000000000000000000000..032d56f45e32c80cff63cbd353fcbfc68638564e --- /dev/null +++ b/products/phone/src/main/ets/entryability/EntryAbilityNew.ets @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* +* 最佳实践: 平板开发实践,一多断点开发实践 +*/ + +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { display, window } from '@kit.ArkUI'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { CommonConstants, WindowUtil } from '@ohos/commons'; +import { BusinessError } from '@kit.BasicServicesKit'; + +// [Start dd_entryability] +export default class EntryAbility extends UIAbility { + private uiContext?: UIContext; + private onWindowSizeChange: (windowSize: window.Size) => void = (windowSize: window.Size) => { + let widthBp: WidthBreakpoint = this.uiContext!.getWindowWidthBreakpoint(); + AppStorage.setOrCreate('currentWidthBreakpoint', widthBp); + let heightBp: HeightBreakpoint = this.uiContext!.getWindowHeightBreakpoint(); + AppStorage.setOrCreate('currentHeightBreakpoint', heightBp); + }; + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + // The system interface depends on UIContext and needs to be invoked after the page is loaded. It needs to be written in the loadContent callback function. + windowStage.getMainWindow().then((data: window.Window) => { + this.uiContext = data.getUIContext(); + let widthBp: WidthBreakpoint = this.uiContext.getWindowWidthBreakpoint(); + let heightBp: HeightBreakpoint = this.uiContext.getWindowHeightBreakpoint(); + AppStorage.setOrCreate('currentWidthBreakpoint', widthBp); + AppStorage.setOrCreate('currentHeightBreakpoint', heightBp); + data.on('windowSizeChange', this.onWindowSizeChange); + }).catch((err: BusinessError) => { + console.error(`Failed to obtain the main window. Cause code: ${err.code}, message: ${err.message}`); + }); + }); + } +} +// [End dd_entryability]