Jelajahi Sumber

Merge remote-tracking branch 'origin/master'

chendeben 1 tahun lalu
induk
melakukan
43b9af8ea4

+ 12 - 0
entry/src/main/ets/common/util/Utility.ets

@@ -865,6 +865,18 @@ export class Utility {
     }
     return list
   }
+  //获取列表有封面的值
+  static getFirstCoverFromList(localList:Array<VideoItem>):string{
+    let list: Array<string> = [];
+    for(let i=0;i<localList.length;i++){
+      let pix = localList[i].pixelMapPath
+      if(pix){
+       return pix
+      }
+
+    }
+    return ''
+  }
 
   //根据type获取对应的播放列表(CommonConstants.TYPE_LOCAL:本地,CommonConstants.TYPE_Dir:私密视频)
   static getGlobalNameList(localList:Array<VideoItem>,type:number){

+ 67 - 50
entry/src/main/ets/pages/NewIndex.ets

@@ -1,4 +1,4 @@
-import { AppUtil, DeviceUtil, DisplayUtil, PreferencesUtil, ToastUtil } from '@pura/harmony-utils';
+import { AppUtil, DeviceUtil, DisplayUtil, LogUtil, PreferencesUtil, ToastUtil } from '@pura/harmony-utils';
 import TitleBar from '../view/TitleBar';
 import { router } from '@kit.ArkUI';
 import mainViewModel, { MainViewModel } from '../viewmodel/MainViewModel';
@@ -89,7 +89,7 @@ struct NewIndex {
     .setLeftTitleBackground($r('app.color.title_bar_bg'))
     .setOnLeftClickListener(() => {
       // 打开菜单栏动画
-      animateTo({ duration: 555 }, () => {
+      this.getUIContext()?.animateTo({ duration: 555 }, () => {
         this.isShowDrawer = true
         this.offsetX = 0
       })
@@ -140,7 +140,7 @@ struct NewIndex {
       } else {
         if (this.isShowDrawer) {
           // 如果抽屉菜单打开,先关闭抽屉
-          animateTo({ duration: 555 }, () => {
+          this.getUIContext()?.animateTo({ duration: 555 }, () => {
             this.isShowDrawer = false
           })
         } else {
@@ -246,15 +246,15 @@ struct NewIndex {
           AboutPage()
             .visibility(this.mType === 4 ? Visibility.Visible : Visibility.None)
           
-          // 抽屉打开时的遮罩层,用于拦截点击事件
-          if (this.isShowDrawer) {
+          // 抽屉打开时的遮罩层,用于拦截点击事件  这个只能正常尺寸的手机竖屏的才能生效
+          if (this.isShowDrawer&&this.isPhonePortrait()) {
             Column()
               .width('100%')
               .height('100%')
               .backgroundColor(Color.Transparent)
               .onClick(() => {
                 // 点击遮罩层关闭抽屉
-                animateTo({ duration: 555 }, () => {
+                this.getUIContext()?.animateTo({ duration: 555 }, () => {
                   this.isShowDrawer = false
                 })
               })
@@ -273,6 +273,20 @@ struct NewIndex {
 
   }
 
+  //是不是正常的手机竖屏
+  isPhonePortrait() {
+    LogUtil.info('twocold this.currentHeightBreakpoint = '+this.currentHeightBreakpoint)
+    LogUtil.info('twocold this.currentWidthBreakpoint = '+this.currentWidthBreakpoint)
+    if(DeviceUtil.getDeviceType() === resourceManager.DeviceType.DEVICE_TYPE_PHONE
+      &&this.currentHeightBreakpoint == HeightBreakpoint.HEIGHT_LG
+      &&this.currentWidthBreakpoint==WidthBreakpoint.WIDTH_SM){//如果是手机横屏,返回false
+
+      return true
+    }
+
+    return false
+  }
+
   /**
    * 判断是否横屏
    * @returns
@@ -295,17 +309,7 @@ struct NewIndex {
   getLeftView() {
     Column() {
       Column() {
-        // 抽屉顶部背景图
-        // Image($r('app.media.bg_music'))
-        //   .width('100%')
-        //   .visibility(this.isCoverOpacity()?Visibility.None:Visibility.Visible)
-        //   .height(180)
-        //   .borderRadius({
-        //     topLeft: 0,
-        //     topRight	: 20,
-        //     bottomLeft: 0,
-        //     bottomRight: 0
-        //   })
+
         this.buildUserInfoCard()
 
         this.getDrawerView()
@@ -342,11 +346,11 @@ struct NewIndex {
         .onActionEnd(() => {
           // 手势结束判断是否关闭抽屉
           if (this.offsetX < -DisplayUtil.getWidth() / 15) {
-            animateTo({ duration: 555 }, () => {
+            this.getUIContext()?.animateTo({ duration: 555 }, () => {
               this.isShowDrawer = false
             })
           } else {
-            animateTo({ duration: 555 }, () => {
+            this.getUIContext()?.animateTo({ duration: 555 }, () => {
               this.isShowDrawer = true
               this.offsetX = 0
             })
@@ -371,24 +375,36 @@ struct NewIndex {
   buildUserInfoCard() {
     Column() {
       Row() {
-        Image(this.userAvatarUrl && this.userAvatarUrl.length > 0 ? this.userAvatarUrl : this.userAvatar)
-          .width(50)
-          .height(50)
-          .margin({ left: 12 })
-          .borderRadius('50%')
-          .clip(true)
-          .backgroundColor(this.isDarkMode ? Color.Black : Color.White)
-          .shadow({
-            radius: 8,
-            color: 0x11000000,
-            offsetX: 0,
-            offsetY: 2
-          })
+        Stack({ alignContent: Alignment.BottomEnd }){
+          Image(this.userAvatarUrl && this.userAvatarUrl.length > 0 ? this.userAvatarUrl : this.userAvatar)
+            .width(55)
+            .height(55)
+            .margin({ left: 12 })
+            .borderRadius('50%')
+            .clip(true)
+            // .fillColor(this.themeColor)
+            .backgroundColor(this.isDarkMode ? Color.Black : Color.White)
+          Column() {
+            Text('VIP')
+              .fontSize(9)
+              .padding(2)
+              .textAlign(TextAlign.Center)
+              .fontWeight(FontWeight.Bolder)
+              .fontColor(Color.White)
+              .visibility(Utility.isNoble()?Visibility.Visible:Visibility.None)
+          }
+          .width(28)
+          .height(16)
+          .borderRadius(15)
+          .backgroundColor(this.themeColor)
+        }
+
+
         Column() {
           Row() {
             Text(this.isLogin ? this.userName : '未登录用户')
-              .fontSize(13)
-              .fontWeight(FontWeight.Bold)
+              .fontSize(14)
+              // .fontWeight(FontWeight.Bold)
               .fontColor(this.isDarkMode ? Color.White : Color.Black)
               .textAlign(TextAlign.Start)
               .maxLines(1)
@@ -400,11 +416,11 @@ struct NewIndex {
 
           if (this.isLogin) {
             if (this.hasActiveSubscription) {
-              Text(this.subscriptionName)
-                .fontSize(13)
-                .fontColor(themeColorWithAlpha(this.themeColor, 0.8, false))
-                .textAlign(TextAlign.Start)
-                .padding({top:5})
+              // Text(this.subscriptionName)
+              //   .fontSize(13)
+              //   .fontColor(themeColorWithAlpha(this.themeColor, 0.8, false))
+              //   .textAlign(TextAlign.Start)
+              //   .padding({top:5})
               this.TimeTextBuilder()
             } else {
               Text('普通用户')
@@ -423,11 +439,12 @@ struct NewIndex {
         .margin({ left: 10 })
 
       }
+      .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.7})
       .onClick(() => {
         // if (!this.isLogin) {
           this.mType = 1
           if (!this.isBigScreen()) {
-            animateTo({ duration: 555 }, () => {
+            this.getUIContext()?.animateTo({ duration: 555 }, () => {
               this.isShowDrawer = false
             })
           // }
@@ -508,7 +525,7 @@ struct NewIndex {
               case MainViewModel.MENU_MUSIC:
                 this.mType = 0
                 if (!this.isBigScreen()) {
-                  animateTo({ duration: 555 }, () => {
+                  this.getUIContext()?.animateTo({ duration: 555 }, () => {
                     this.isShowDrawer = false
                   })
                 }
@@ -516,7 +533,7 @@ struct NewIndex {
               case MainViewModel.MENU_FILE_SCAN:
                 this.mType = 2
                 if (!this.isBigScreen()) {
-                  animateTo({ duration: 555 }, () => {
+                  this.getUIContext()?.animateTo({ duration: 555 }, () => {
                     this.isShowDrawer = false
                   })
                 }
@@ -524,32 +541,32 @@ struct NewIndex {
             // case MainViewModel.MENU_HOME:
             //   this.mType = 0
             //   this.modeType = 0
-            //   animateTo({ duration: 555 }, () => {
+            //   this.getUIContext()?.animateTo({ duration: 555 }, () => {
             //     this.isShowDrawer = false
             //   })
             //   break
               case MainViewModel.MENU_MIEDIA_KU:
                 this.modeType = 1
-                animateTo({ duration: 555 }, () => {
+                this.getUIContext()?.animateTo({ duration: 555 }, () => {
                   this.isShowDrawer = false
                 })
                 break
               case MainViewModel.MENU_MIEDIA_ARTIST:
                 this.modeType = 2
-                animateTo({ duration: 555 }, () => {
+                this.getUIContext()?.animateTo({ duration: 555 }, () => {
                   this.isShowDrawer = false
                 })
                 break
               case MainViewModel.MENU_MIEDIA_ALBUM:
                 this.modeType = 3
-                animateTo({ duration: 555 }, () => {
+                this.getUIContext()?.animateTo({ duration: 555 }, () => {
                   this.isShowDrawer = false
                 })
                 break
               case MainViewModel.MENU_SETTING:
                 this.mType = 3
                 if (!this.isBigScreen()) {
-                  animateTo({ duration: 555 }, () => {
+                  this.getUIContext()?.animateTo({ duration: 555 }, () => {
                     this.isShowDrawer = false
                   })
                 }
@@ -565,7 +582,7 @@ struct NewIndex {
               case MainViewModel.MENU_USER:
                 this.mType = 1
                 if (!this.isBigScreen()) {
-                  animateTo({ duration: 555 }, () => {
+                  this.getUIContext()?.animateTo({ duration: 555 }, () => {
                     this.isShowDrawer = false
                   })
                 }
@@ -575,7 +592,7 @@ struct NewIndex {
                 break
               case MainViewModel.MENU_NET_CONNECT:
                 this.mType = 1
-                animateTo({ duration: 555 }, () => {
+                this.getUIContext()?.animateTo({ duration: 555 }, () => {
                   this.isShowDrawer = false
                 })
                 break
@@ -611,7 +628,7 @@ struct NewIndex {
               case MainViewModel.MENU_ABOUT:
                 this.mType = 4
                 if (!this.isBigScreen()) {
-                  animateTo({ duration: 555 }, () => {
+                  this.getUIContext()?.animateTo({ duration: 555 }, () => {
                     this.isShowDrawer = false
                   })
                 }

+ 62 - 2
entry/src/main/ets/pages/SettingPage.ets

@@ -51,6 +51,7 @@ export struct SettingPage {
   static readonly IS_COVER_TOP_BIG: string = 'IS_COVER_TOP_BIG';
   public static THEME_COLOR_KEY: string = 'THEME_COLOR';
   public static TWO_FINGER_TYPE: string = 'twoFingerType';
+  public static LONG_PRESS_SPEED: string = 'longPressSpeed';
   public static THEME_COLOR_LIST: Array<ThemeColorItem> = [
     { name: '玫瑰粉', color: '#FF4081', isVip: false },
     { name: '经典蓝', color: '#0A59F7', isVip: false },
@@ -92,6 +93,7 @@ export struct SettingPage {
   @State isCustomizeBgSheet: boolean = false //自定义背景界面
   @State isShowTitleBar: boolean = true //是否显示分类导航条
   @State isShowAllBar: boolean = true //是否显示播放全部条
+  @State longPressSpeed: number = 3 //长按默认倍数
   @State blurValue: number = 0 //背景模糊
   @State bgBrightness: number = 0 //背景亮度
   @State isGridMusic: boolean = true //是否网格布局
@@ -216,6 +218,7 @@ export struct SettingPage {
     this.isShowPlayPageBack = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_PLAYPAGE_BACK, false)
     this.isShowSingleLineLyric = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_SLLYRIC, false)
     this.isCoverTopBig = PreferencesUtil.getBooleanSync(SettingPage.IS_COVER_TOP_BIG, false)
+    this.longPressSpeed = PreferencesUtil.getNumberSync(SettingPage.LONG_PRESS_SPEED, 3)
     // 只用 themeMode 控制主题
     this.themeMode = PreferencesUtil.getNumberSync(SettingPage.THEME_MODE, 0)
     this.applyThemeMode(this.themeMode)
@@ -641,6 +644,7 @@ export struct SettingPage {
               Toggle({ type: ToggleType.Switch, isOn: this.isScrollHide })
                 .selectedColor(this.themeColor)
                 .switchPointColor(Color.White)
+                .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                 .margin({ right: 18 })
                 .onChange((checked: boolean) => {
                   this.isScrollHide = checked;
@@ -657,7 +661,7 @@ export struct SettingPage {
 
             // 网格布局
             Row() {
-              Text('网格布局')
+              Text('网格模式')
                 .margin({ left: 18 })
                 .fontSize(15)
                 .fontColor(Color.Gray)
@@ -667,6 +671,7 @@ export struct SettingPage {
                 .selectedColor(this.themeColor)
                 .switchPointColor(Color.White)
                 .margin({ right: 18 })
+                .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                 .onChange((checked: boolean) => {
                   this.isGridMusic = checked;
                   PreferencesUtil.put(SettingPage.IS_GRID_MUSIC, this.isGridMusic)
@@ -745,6 +750,7 @@ export struct SettingPage {
                 .selectedColor(this.themeColor)
                 .switchPointColor(Color.White)
                 .margin({ right: 18 })
+                .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                 .onChange((checked: boolean) => {
                   this.isCoverTopBig = checked;
                   if(this.isCoverTopBig){
@@ -773,6 +779,7 @@ export struct SettingPage {
               Toggle({ type: ToggleType.Switch, isOn: this.isCircleBtn })
                 .selectedColor(this.themeColor)
                 .switchPointColor(Color.White)
+                .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                 .margin({ right: 18 })
                 .onChange((checked: boolean) => {
                   this.isCircleBtn = checked;
@@ -825,6 +832,7 @@ export struct SettingPage {
               Toggle({ type: ToggleType.Switch, isOn: this.isStartAutoPlay })
                 .selectedColor(this.themeColor)
                 .switchPointColor(Color.White)
+                .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                 .margin({ right: 18 })
                 .onChange((checked: boolean) => {
                   this.isStartAutoPlay = checked;
@@ -848,6 +856,7 @@ export struct SettingPage {
               Toggle({ type: ToggleType.Switch, isOn: this.isMemoryLastPlay })
                 .selectedColor(this.themeColor)
                 .switchPointColor(Color.White)
+                .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                 .margin({ right: 18 })
                 .onChange((checked: boolean) => {
                   this.isMemoryLastPlay = checked;
@@ -872,6 +881,7 @@ export struct SettingPage {
               Toggle({ type: ToggleType.Switch, isOn: this.isMusicMemoryPlay })
                 .selectedColor(this.themeColor)
                 .switchPointColor(Color.White)
+                .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                 .margin({ right: 18 })
                 .onChange((checked: boolean) => {
                   this.isMusicMemoryPlay = checked;
@@ -884,6 +894,46 @@ export struct SettingPage {
             .height(55)
             .clickEffect({ level: ClickEffectLevel.HEAVY })
 
+
+            Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
+
+            // 长按默认倍数
+            Row() {
+              Text('长按默认倍数')
+                .margin({ left: 18 })
+                .fontSize(15)
+                .fontColor(Color.Gray)
+                .fontWeight(480)
+                .layoutWeight(1)
+              Select([//倍速
+                { value: '0.25x' },
+                { value: '0.5x' },
+                { value: '0.75x' },
+                { value: '1x' },
+                { value: '1.25x' },
+                { value: '1.5x' },
+                { value: '1.75x' },
+                { value: '2x' },
+                { value: '2.5x' },
+                { value: '3x' }])
+                .font({ size: 15, weight: FontWeight.Medium })
+                .fontColor(Color.Gray)
+                .margin({right:18})
+                .selected(CommonConstants.video_speed_list.indexOf(this.longPressSpeed))
+                .value(Utility.optimizedFormat(this.longPressSpeed))
+                .onSelect(async (_index: number, text?: string | undefined) => {
+                  let speed = parseFloat(text?.replace('x', '') || '1');
+                  if (!CommonConstants.video_speed_list.includes(speed)) {
+                    speed = 1;
+                  }
+                  this.longPressSpeed = speed
+                  PreferencesUtil.put(SettingPage.LONG_PRESS_SPEED, this.longPressSpeed)
+                  this.sendChangeEvent()
+                })
+            }
+            .height(55)
+            .clickEffect({level:ClickEffectLevel.HEAVY})
+
             Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
             // 默认排序模式
             Row() {
@@ -927,6 +977,7 @@ export struct SettingPage {
                 .layoutWeight(1)
               Toggle({ type: ToggleType.Switch, isOn: this.isSavePlayMode })
                 .selectedColor(this.themeColor)
+                .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                 .switchPointColor(Color.White)
                 .margin({ right: 18 })
                 .onChange((checked: boolean) => {
@@ -952,6 +1003,7 @@ export struct SettingPage {
                 .selectedColor(this.themeColor)
                 .switchPointColor(Color.White)
                 .margin({ right: 18 })
+                .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                 .onChange((checked: boolean) => {
                   this.isSameTimePlay = checked;
                   PreferencesUtil.put(SettingPage.IS_SAMETIME_PLAY, this.isSameTimePlay)
@@ -975,6 +1027,7 @@ export struct SettingPage {
               Toggle({ type: ToggleType.Switch, isOn: this.isMusicBGCover })
                 .selectedColor(this.themeColor)
                 .switchPointColor(Color.White)
+                .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                 .margin({ right: 18 })
                 .onChange((checked: boolean) => {
                   this.isMusicBGCover = checked;
@@ -1061,6 +1114,7 @@ export struct SettingPage {
           .layoutWeight(1)
         Toggle({ type: ToggleType.Switch, isOn: this.isShowTitleBar })
           .selectedColor(this.themeColor)
+          .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
           .switchPointColor(Color.White)
           .margin({ right: 18 })
           .onChange((checked: boolean) => {
@@ -1087,6 +1141,7 @@ export struct SettingPage {
           .selectedColor(this.themeColor)
           .switchPointColor(Color.White)
           .margin({ right: 18 })
+          .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
           .onChange((checked: boolean) => {
             this.isShowAllBar = checked;
             PreferencesUtil.put(SettingPage.IS_SHOW_ALLBAR, this.isShowAllBar)
@@ -1111,6 +1166,7 @@ export struct SettingPage {
           .selectedColor(this.themeColor)
           .switchPointColor(Color.White)
           .margin({ right: 18 })
+          .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
           .onChange((checked: boolean) => {
             this.isShowSimi = checked;
             PreferencesUtil.put(SettingPage.IS_SHOW_SIMI, this.isShowSimi)
@@ -1135,6 +1191,7 @@ export struct SettingPage {
           .selectedColor(this.themeColor)
           .switchPointColor(Color.White)
           .margin({ right: 18 })
+          .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
           .onChange((checked: boolean) => {
             this.isShowSingleLineLyric = checked;
             PreferencesUtil.put(SettingPage.IS_SHOW_SLLYRIC, this.isShowSingleLineLyric)
@@ -1159,6 +1216,7 @@ export struct SettingPage {
           .selectedColor(this.themeColor)
           .switchPointColor(Color.White)
           .margin({ right: 18 })
+          .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
           .onChange((checked: boolean) => {
             this.isShowPlayPageBack = checked;
             PreferencesUtil.put(SettingPage.IS_SHOW_PLAYPAGE_BACK, this.isShowPlayPageBack)
@@ -1183,6 +1241,7 @@ export struct SettingPage {
           .selectedColor(this.themeColor)
           .switchPointColor(Color.White)
           .margin({ right: 18 })
+          .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
           .onChange((checked: boolean) => {
             this.isShowFAV = checked;
             PreferencesUtil.put(SettingPage.IS_SHOW_FAV, this.isShowFAV)
@@ -1207,6 +1266,7 @@ export struct SettingPage {
           .selectedColor(this.themeColor)
           .switchPointColor(Color.White)
           .margin({ right: 18 })
+          .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
           .onChange((checked: boolean) => {
             this.isShowHistory = checked;
             PreferencesUtil.put(SettingPage.IS_SHOW_HISTORY, this.isShowHistory)
@@ -1469,7 +1529,7 @@ export struct SettingPage {
           Toggle({ type: ToggleType.Switch, isOn: this.isCustomizeBg })
             .selectedColor(this.themeColor)
             .switchPointColor(Color.White)
-            .clickEffect({ level: ClickEffectLevel.HEAVY })
+            .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
             .margin({ right: 18 })
             .onChange((checked: boolean) => {
               this.isCustomizeBg = checked;

+ 33 - 14
entry/src/main/ets/pages/UserCenter.ets

@@ -466,23 +466,35 @@ export struct UserCenter {
   buildUserInfoCard() {
     Column() {
       Row() {
-        Image(this.userAvatarUrl && this.userAvatarUrl.length > 0 ? this.userAvatarUrl : this.userAvatar)
-          .width(56)
-          .height(56)
-          .borderRadius('50%')
-          .clip(true)
-          .backgroundColor(this.isDarkMode ? Color.Black : Color.White)
-          .shadow({
-            radius: 8,
-            color: 0x11000000,
-            offsetX: 0,
-            offsetY: 2
-          })
+        Stack({ alignContent: Alignment.BottomEnd }){
+          Image(this.userAvatarUrl && this.userAvatarUrl.length > 0 ? this.userAvatarUrl : this.userAvatar)
+            .width(55)
+            .height(55)
+            .margin({ left: 12 })
+            .borderRadius('50%')
+            .clip(true)
+            .backgroundColor(this.isDarkMode ? Color.Black : Color.White)
+          Column() {
+            Text('VIP')
+              .fontSize(9)
+              .padding(2)
+              .textAlign(TextAlign.Center)
+              .fontWeight(FontWeight.Bolder)
+              .fontColor(Color.White)
+              .visibility(Utility.isNoble()?Visibility.Visible:Visibility.None)
+          }
+          .width(28)
+          .height(16)
+          .borderRadius(15)
+          .backgroundColor(this.themeColor)
+        }
+
         Column() {
           Row() {
             Text(this.isLogin ? this.userName : '未登录用户')
-              .fontSize(17)
+              .fontSize(14)
               .fontWeight(FontWeight.Bold)
+              .padding(5)
               .fontColor(this.isDarkMode ? Color.White : Color.Black)
               .textAlign(TextAlign.Start)
               .maxLines(1)
@@ -496,15 +508,18 @@ export struct UserCenter {
             if (this.hasActiveSubscription) {
               Text(this.subscriptionName)
                 .fontSize(13)
+                .padding(5)
                 .fontColor(themeColorWithAlpha(this.themeColor, 0.8, false))
                 .textAlign(TextAlign.Start)
               Text('到期:' + this.subscriptionEndDate)
                 .fontSize(11)
+                .padding(5)
                 .fontColor(this.isDarkMode ? Color.White : Color.Gray)
                 .textAlign(TextAlign.Start)
             } else {
               Text('普通用户')
-                .fontSize(12)
+                .fontSize(13)
+                .padding(5)
                 .fontColor(this.isDarkMode ? Color.White : Color.Grey)
                 .textAlign(TextAlign.Start)
             }
@@ -522,6 +537,7 @@ export struct UserCenter {
               .height(36)
               .padding({ left:20,right:20,top:10,bottom:10 })
               .fontSize(13)
+              .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
               .borderRadius(13)
               .margin({ left: 0 })
               .onClick(() => {
@@ -549,6 +565,7 @@ export struct UserCenter {
 
             Button('登录')
               .fontColor(Color.White)
+              .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
               .backgroundColor(this.isDarkMode ? $r('app.color.title_bar_bg') : this.themeColor)
               .height(36)
               .padding({ left:20,right:20,top:10,bottom:10 })
@@ -686,6 +703,7 @@ export struct UserCenter {
             .width('100%')
             .height(145)
           }
+
         })
       }
       .columnsTemplate('1fr 1fr')
@@ -883,6 +901,7 @@ export struct UserCenter {
         .fontColor(Color.White)
         .fontSize(18)
         .padding(12)
+        .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
         .fontWeight(FontWeight.Bold)
         .margin({ top: 1, bottom: 15 })
         .alignSelf(ItemAlign.Center)

+ 204 - 62
entry/src/main/ets/view/LocalMusic.ets

@@ -369,7 +369,7 @@ export struct LocalMusic {
       this.showPupDialog()
     })
     .setOnLeftClickListener(() => {
-      this.doSwipBack()
+        this.doSwipBack()
     })
 
   doSwipBack() {
@@ -414,10 +414,24 @@ export struct LocalMusic {
       })
       return
     }
+    //返回上一级歌单的滚动位置
+    setTimeout(() => {
+      if(this.modeType ==0){
+        if (this.isGridMusic) {
+          let lastOffset = this.mScrollMap.get('lastGridDirScrollOffset') ?? 0
+          this.scroller.scrollTo({ xOffset: 0, yOffset: lastOffset })
+        } else {
+          let lastOffset = this.mScrollMap.get('lastListDirScrollOffset') ?? 0
+          this.listScroller.scrollTo({ xOffset: 0, yOffset: lastOffset })
+        }
+      }
+    }, 300)
+
 
     this.currentPath = Utility.getParentDirectory(this.currentPath)
     Logger.info('this.currentPath2 = ' + this.currentPath)
     this.getSortedFiles(this.currentPath)
+
   }
 
   // 组件生命周期
@@ -444,7 +458,9 @@ export struct LocalMusic {
     // 监听广播事件(打开其他应用处理)
     emitter.on(eventUpdateSwipeBack, (eventData: emitter.EventData) => {
 
-      this.doSwipBack()
+      animateTo({ duration: 555 }, () => {
+        this.doSwipBack()
+      })
 
     });
 
@@ -577,6 +593,7 @@ export struct LocalMusic {
     this.isShowPlayPageBack = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_PLAYPAGE_BACK, false)
     this.isCoverTopBig = PreferencesUtil.getBooleanSync(SettingPage.IS_COVER_TOP_BIG, false)
     this.isShowSingleLineLyric = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_SLLYRIC, false)
+    this.longPressSpeed = PreferencesUtil.getNumberSync(SettingPage.LONG_PRESS_SPEED, 3)
     if (this.isSavePlayMode) {
       this.playType = PreferencesUtil.getNumberSync('musicPlayType', 0)
     }
@@ -814,7 +831,7 @@ export struct LocalMusic {
     if (curPath === this.rootPath) {
       this.titleBarModel.setLeftIconMain($r('app.media.menu'))
     } else {
-
+      this.currentTitleName = Utility.getFileDirName(this.currentPath, this.rootPath)
       if (this.rootPath === Utility.getParentDirectory(curPath)) {
         this.titleBarModel.setLeftIconMain($r('app.media.left_back_white'))
       }
@@ -835,9 +852,7 @@ export struct LocalMusic {
       // this.videoLocalList = cachedValue;
       Logger.info('onecold 有缓存 cachedValue = ' + cachedValue)
       if(this.currentPath !=this.rootPath){//第一个封面赋值给currentTitleCover
-        if(cachedValue[0].pixelMapPath){
-          this.currentTitleCover = cachedValue[0].pixelMapPath
-        }
+        this.currentTitleCover = Utility.getFirstCoverFromList(cachedValue)
       }
       this.updateListData(cachedValue)
     }
@@ -910,9 +925,8 @@ export struct LocalMusic {
 
       if (ArrayUtil.isNotEmpty(files)) {
         if(this.currentPath !=this.rootPath){
-          if(files[0].pixelMapPath){
-            this.currentTitleCover = files[0].pixelMapPath//第一个封面赋值给currentTitleCover
-          }
+          if(cachedValue)
+            this.currentTitleCover = Utility.getFirstCoverFromList(cachedValue)
         }
         // 缓存结果
         this.addCache(curPath, this.videoLocalList);
@@ -1902,7 +1916,7 @@ export struct LocalMusic {
 
     Scroll() {
       Column() {
-        if(!this.isShowCoverTitle()){
+        if(!this.isShowCoverHeader()){
           Column(){
             Line().width('100%').height('100%')
           }
@@ -1986,18 +2000,57 @@ export struct LocalMusic {
 
             Column() {
 
-              if(this.isShowCoverTitle()){
-                // this.coverTitle()
-              }else{
+              if(!this.isShowCoverHeader()){
                 this.tabTitle()
                 this.listViewTitle()
               }
 
               if(this.isGridMusic){
-                this.getGridView()
+                if(this.isShowCoverHeader()){
+                  Scroll(){
+                    Column(){
+                      this.coverHeader()
+                      this.getGridView()
+                    }
+
+                  }
+                  // .height(px2vp(DisplayUtil.getHeight()-AppUtil.getStatusBarHeight()-AppUtil.getNavigationIndicatorHeight()))
+                }else{
+                  this.getGridView()
+                }
+
               }else {
                 this.getListView()
               }
+              if(this.modeType==0){
+                Column() {
+                  Button('同步数据', { type: ButtonType.Capsule, stateEffect: true })
+                    .width(130)
+                    .height(50)
+                    .margin({ top: 100, bottom: 40 })
+                    .backgroundColor(this.themeColor)
+                    .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
+                    .onClick(() => {
+                      this.asyncCurrentPathData()
+                    })
+
+                }
+                .height(100)
+                .position({top:260})
+                .transition(TransitionEffect.asymmetric(TransitionEffect.scale({ x: 1.2, y: 1.2 }).animation({ duration: 500 }),
+                  TransitionEffect.scale({ x: 0, y: 0 })  ))
+                .justifyContent(FlexAlign.Center)
+                .opacity(this.isZero ? 1 : 0)
+                .visibility(this.isZero&&!this.isFavMusic&&!this.isHistory
+                  && this.modeType === 0 ? Visibility.Visible : Visibility.None)
+                .animation({
+                  duration: 500,
+                  curve: 'ease-in-out' // 可选动画曲线
+                })
+                .width('100%')
+              }
+
+
             }
             .layoutWeight(1)
             .height('100%')
@@ -2091,33 +2144,10 @@ export struct LocalMusic {
             duration: 666,
             curve: 'ease-in-out' // 可选动画曲线
           })
-          .position(this.isShowCoverTitle()?{ bottom:78}
+          .position(this.isShowCoverHeader()?{ bottom:78}
             :{ bottom:this.isPhoneLan()?130: 168 }) // 将 Row 固定在底部
 
-          if(this.modeType==0){
-            Column() {
-              Button('同步数据', { type: ButtonType.Capsule, stateEffect: true })
-                .width(130)
-                .height(50)
-                .margin({ top: 50, bottom: 40 })
-                .backgroundColor(this.themeColor)
 
-                .onClick(() => {
-                  this.asyncCurrentPathData()
-                })
-
-            }
-            .height('60%')
-            .justifyContent(FlexAlign.Center)
-            .opacity(this.isZero ? 1 : 0)
-            .visibility(this.isZero&&!this.isFavMusic&&!this.isHistory
-              && this.modeType === 0 ? Visibility.Visible : Visibility.None)
-            .animation({
-              duration: 666,
-              curve: 'ease-in-out' // 可选动画曲线
-            })
-            .width('100%')
-          }
         }
         .width('100%')
         .height('100%')
@@ -2655,21 +2685,21 @@ export struct LocalMusic {
   @State currentTitleName:string = ''//点击歌单,艺术家,专辑进去后的标题
   @State currentTitleCover:string|ResourceStr = ''//点击歌单,艺术家,专辑进去后的封面
 
-  isShowCoverTitle(){
+  isShowCoverHeader(){
     if(this.modeType==0){
       if(this.isHistory||this.isFavMusic){
         return true
       }
-      if (this.currentPath != this.rootPath&&!this.isGridMusic ) {
+      if (this.currentPath != this.rootPath) {
         return true
       }
     }
-    return (this.modeType==2||this.modeType==3)&&this.isCanBack&&!this.isGridMusic
+    return (this.modeType==2||this.modeType==3)&&this.isCanBack
   }
 
   @Builder
-  coverTitle() {
-    if(this.isShowCoverTitle()){
+  coverHeader() {
+    if(this.isShowCoverHeader()){
       Column(){
 
         Stack() {
@@ -2691,7 +2721,7 @@ export struct LocalMusic {
                 })
               })
             Blank().flexGrow(1)
-            if(this.modeType==0){
+            if(this.modeType==0&&!this.isHistory&&!this.isFavMusic){
               Image($r('app.media.add'))
                 .width(25)
                 .height(25)
@@ -2730,7 +2760,7 @@ export struct LocalMusic {
                 .fontSize(16)
                 .maxLines(1)
                 .fontWeight(FontWeight.Bold)
-                .fontColor(Color.White)
+                .fontColor($r('app.color.text_color'))
                 .margin({ left: this.currentLyricAlignMode === 0 ? 18 : 0 })
               if(this.modeType==3&&ArrayUtil.isNotEmpty(this.videoLocalList)){
                 Text(`艺术家:${this.videoLocalList[0].artist}`)
@@ -2738,7 +2768,7 @@ export struct LocalMusic {
                   .padding({ top: 8 })
                   .maxLines(1)
                   .fontWeight(FontWeight.Bold)
-                  .fontColor(Color.White)
+                  .fontColor($r('app.color.text_color'))
                   .margin({ left: this.currentLyricAlignMode === 0 ? 18 : 0 })
               }
               Row() {
@@ -2746,7 +2776,7 @@ export struct LocalMusic {
                   .fontSize(13)
                   .fontWeight(FontWeight.Bold)
                   .padding({ top: 8 })
-                  .fontColor(Color.White)
+                  .fontColor($r('app.color.text_color'))
                   .margin({ left: this.currentLyricAlignMode === 0 ? 18 : 0 })
                 Blank()
 
@@ -2776,8 +2806,10 @@ export struct LocalMusic {
       .padding({top:this.topRectHeight ,bottom:this.bottomBarHeight+48  })
       .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
       .justifyContent(FlexAlign.SpaceBetween)
+      // .transition({ type: TransitionType.Insert, translate: { x: -DisplayUtil.getWidth(), y: 0 } })
+      // .transition({ type: TransitionType.Delete, translate: { x: -DisplayUtil.getWidth(), y: 0 } })
       // .transition(TransitionEffect.scale({ x: 1.2, y: 1.2 }).animation({ duration: 500 }))
-      .transition(TransitionEffect.asymmetric(TransitionEffect.scale({ x: 1.2, y: 1.2 }).animation({ duration: 500 }),
+      .transition(TransitionEffect.asymmetric(TransitionEffect.scale({ x: 0.7, y: 0.7}).animation({ duration: 500 }),
         TransitionEffect.scale({ x: 0, y: 0 })  ))
       .clickEffect({level:ClickEffectLevel.LIGHT})
       // .animation({
@@ -3233,7 +3265,6 @@ export struct LocalMusic {
         GridItem() {
           Stack({ alignContent: Alignment.Center }) {
             this.MusicItemGrid(item,index)
-
           }
         }
         // .transition(TransitionEffect.scale({ x: 0.8, y: 0.8 }).animation({ duration: 500 }))
@@ -3314,7 +3345,9 @@ export struct LocalMusic {
     .editMode(true)
     .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
       .animation({ duration: 500, curve: Curve.Ease }))
-    .margin({ bottom: this.isCoverOpacity() ? 80 : 175 })
+    // .margin({ bottom: this.isCoverOpacity() ? 80 : 175 })
+    .margin({ bottom: this.isCoverOpacity() ? (this.isShowCoverHeader()? 40:80)
+      :(this.isShowCoverHeader()? 90:175) })
     .layoutWeight(1)
     .scrollBar(BarState.Off)
     .supportAnimation(true)
@@ -3683,7 +3716,7 @@ export struct LocalMusic {
   getListView() {
 
     List({ scroller: this.listScroller }) {
-      ListItemGroup({ header: this.coverTitle() }) {
+      ListItemGroup({ header: this.coverHeader() }) {
       LazyForEach(this.dataSource, (item: VideoItem, index: number) => {
         ListItem() {
           Column() {
@@ -3850,12 +3883,14 @@ export struct LocalMusic {
 
       }))
     // .divider({ strokeWidth: 1, color: this.isDarkMode? '#333333':'#ffe9f0f0' })
-    .margin({ bottom: this.isCoverOpacity() ? (this.isShowCoverTitle()? 20:80)
-      :(this.isShowCoverTitle()? 70:166) })
+    .margin({ bottom: this.isCoverOpacity() ? (this.isShowCoverHeader()? 20:80)
+      :(this.isShowCoverHeader()? 70:166) })
     .cachedCount(6)
     .borderRadius(20)
-    .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
-      .animation({ duration: 500, curve: Curve.Ease }))
+    .transition(TransitionEffect.asymmetric(TransitionEffect.scale({ x: 1.2, y: 1.2 }).animation({ duration: 500 }),
+      TransitionEffect.scale({ x: 0, y: 0 })  ))
+    // .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
+    //   .animation({ duration: 500, curve: Curve.Ease }))
     .layoutWeight(1)
     .onScrollIndex((start) => {
       this.selectedIndex = start
@@ -3987,6 +4022,26 @@ export struct LocalMusic {
   private doPlay(item: VideoItem, index?: number, isFromSonPlayList?: boolean) {
     switch (item.type) {
       case CommonConstants.TYPE_IS_DIR:
+        // 进入歌单前安全保存当前滚动偏移量,支持列表和网格
+        let offsetB = 0
+        if (this.isGridMusic) {
+          if (this.scroller && typeof this.scroller.currentOffset === 'function') {
+            let cur = this.scroller.currentOffset()
+            if (cur && typeof cur.yOffset === 'number') {
+              offsetB = cur.yOffset
+            }
+          }
+          this.mScrollMap.set('lastGridDirScrollOffset', offsetB)
+        } else {
+          if (this.listScroller && typeof this.listScroller.currentOffset === 'function') {
+            let cur = this.listScroller.currentOffset()
+            if (cur && typeof cur.yOffset === 'number') {
+              offsetB = cur.yOffset
+            }
+          }
+          this.mScrollMap.set('lastListDirScrollOffset', offsetB)
+        }
+
         if (item.name === LocalMusic.STR_LOCK_VIDEO) {
           this.showVerifyDialog()
           this.currentTitleName = '私密音频'
@@ -4001,6 +4056,8 @@ export struct LocalMusic {
 
           if (ArrayUtil.isEmpty(this.historyList)) {
             ToastUtil.showToast('最近播放记录空空如也')
+          }else{
+            this.currentTitleCover = Utility.getFirstCoverFromList(this.historyList)
           }
           this.isHistory = true
           this.titleBarModel.setLeftIconMain($r('app.media.left_back_white'))
@@ -4012,14 +4069,26 @@ export struct LocalMusic {
           this.updateListData(this.favList)
           if(ArrayUtil.isEmpty(this.favList)){
             ToastUtil.showToast('无音乐收藏记录')
+          }else{
+            this.currentTitleCover = Utility.getFirstCoverFromList(this.favList)
           }
           this.isFavMusic = true
           this.titleBarModel.setLeftIconMain($r('app.media.left_back_white'))
         }else {
-          this.currentTitleName = '歌单:'+item.name
+          this.currentTitleName = item.name
           this.currentPath = item.filePath
           this.currentTitleCover = $r('app.media.ic_avatar5')
           this.getSortedFiles(this.currentPath)
+          //进入歌单的时候的滚动位置在顶部
+          setTimeout(() => {
+            if(this.modeType ==0){
+              if (this.isGridMusic) {
+                this.scroller.scrollTo({ xOffset: 0, yOffset: 0 })
+              } else {
+                this.listScroller.scrollTo({ xOffset: 0, yOffset: 0 })
+              }
+            }
+          }, 300)
         }
 
         break;
@@ -4945,6 +5014,7 @@ export struct LocalMusic {
       .width('100%')
       .padding(16)
     }
+    .margin({bottom:18})
   }
 
   // 获取显示文本
@@ -5452,6 +5522,28 @@ export struct LocalMusic {
     return false
   }
 
+  @State showSpeedDialog:boolean = false
+  @State longPressSpeed: number = 3 //长按默认倍数
+  @Builder
+  SpeedDialog() {
+    Stack() {
+      Column(){
+        Text('已切换至' +this.longPressSpeed + '倍速播放')
+          .fontColor(Color.White)
+          .visibility(Visibility.Hidden)
+      }
+      .backgroundColor(Color.Black)
+      .borderRadius(20)
+      .padding({ top:10,right:20,left:15,bottom:20 })
+      .opacity(0.6)
+
+      Text('已切换至' +this.longPressSpeed + '倍速播放')
+        .fontColor(Color.White)
+    }
+    .transition(TransitionEffect.OPACITY.animation({ duration: 500, curve: Curve.Ease }))
+    .zIndex(5)
+    .margin({ top: 80 })
+  }
 
   @Builder
   IjkMusicPlayerView() {
@@ -5493,6 +5585,50 @@ export struct LocalMusic {
           // .visibility(this.currentBreakpoint !== BreakpointTypeEnum.SM
           //   ?Visibility.Visible:Visibility.Visible)
 
+          if (this.showSpeedDialog) {
+            this.SpeedDialog()
+          }
+
+          Stack(){
+            //手势拖动快进
+            Column()
+              .width(CommonConstants.FULL_PERCENT)
+              .height('100%')
+              .gesture(
+                GestureGroup(GestureMode.Parallel,
+                  // 长按屏幕倍数播放
+                  LongPressGesture({ repeat: true })
+                    .onAction(() => {
+                      if (this.CONTROL_PlayStatus === PlayStatus.PLAY) {
+                        this.showSpeedDialog = true;
+                        this.mIjkMediaPlayer.setSpeed(this.longPressSpeed+'f');
+                      }
+                    })
+                    .onActionEnd(() => {
+                      this.showSpeedDialog = false;
+                      this.mIjkMediaPlayer.setSpeed(this.playSpeed  +'f');
+                    }),
+
+
+                  //双击屏幕暂停或者播放
+                  TapGesture({ count: 2 })
+                    .onAction((event: GestureEvent) => {
+                      if (this.CONTROL_PlayStatus === PlayStatus.PLAY) {
+                        ToastUtil.showToast('已暂停')
+                      }
+                      this.playOrPause()
+
+                    })
+
+                )
+
+
+
+              )
+
+          }
+          .height('65%')
+
           Swiper() {
             this.CoverInfo()
             this.PlayerLyrics()
@@ -6896,6 +7032,7 @@ export struct LocalMusic {
             Toggle({ type: ToggleType.Switch, isOn: this.isOpenPip })
               .selectedColor(this.themeColor)
               .switchPointColor(Color.White)
+              .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
               .margin({ right: 18 })
               .onChange((checked: boolean) => {
                 this.isOpenPip = checked;
@@ -7037,6 +7174,7 @@ export struct LocalMusic {
             Toggle({ type: ToggleType.Switch, isOn: this.isHightLightCenter })
               .selectedColor(this.themeColor)
               .switchPointColor(Color.White)
+              .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
               .onChange((checked: boolean) => {
                 this.isHightLightCenter = checked;
                 PreferencesUtil.put(SettingPage.IS_SHOW_SIMI, this.isHightLightCenter)
@@ -7795,7 +7933,7 @@ export struct LocalMusic {
                   .fontSize(16)
                   .fontColor(Color.White)
                   .bindSheet($$this.isShowDetailMore, this.detailSheet(), {
-                    height: this.isCoverOpacity() ? '95%' : '65%',
+                    height: this.isCoverOpacity() ? '95%' : '75%',
                     dragBar: true,
                     preferType: this.currentBreakpoint!==BreakpointTypeEnum.SM?SheetType.CENTER:SheetType.BOTTOM,
                     showClose: true,
@@ -7959,6 +8097,7 @@ export struct LocalMusic {
                   .selectedColor(this.themeColor)
                   .switchPointColor(Color.White)
                   .margin({ right: 15 })
+                  .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                   .visibility(more.id == 6 ? Visibility.Visible : Visibility.None)
                   .onChange((checked: boolean) => { // 选择开关状态变化时触发事件
                     if (more.id == 6) {
@@ -7977,6 +8116,7 @@ export struct LocalMusic {
                 Toggle({ type: ToggleType.Switch, isOn: this.isMusicBGCover })
                   .selectedColor(this.themeColor)
                   .switchPointColor(Color.White)
+                  .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                   .margin({ right: 15 })
                   .visibility(more.id == 7 ? Visibility.Visible : Visibility.None)
                   .onChange((checked: boolean) => { // 选择开关状态变化时触发事件
@@ -7997,6 +8137,7 @@ export struct LocalMusic {
                 Toggle({ type: ToggleType.Switch, isOn: this.isSavePlayMode })
                   .selectedColor(this.themeColor)
                   .switchPointColor(Color.White)
+                  .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                   .margin({ right: 15 })
                   .visibility(more.id == 8 ? Visibility.Visible : Visibility.None)
                   .onChange((checked: boolean) => { // 选择开关状态变化时触发事件
@@ -8017,6 +8158,7 @@ export struct LocalMusic {
                 Toggle({ type: ToggleType.Switch, isOn: this.isCoverRectangle })
                   .selectedColor(this.themeColor)
                   .switchPointColor(Color.White)
+                  .clickEffect({level:ClickEffectLevel.LIGHT, scale: 0.5})
                   .margin({ right: 15 })
                   .visibility(more.id == 13 ? Visibility.Visible : Visibility.None)
                   .onChange((checked: boolean) => { // 选择开关状态变化时触发事件
@@ -9636,11 +9778,11 @@ export struct LocalMusic {
   }
 
   private seekTo(value: string) {
-    // this.stopProgressTask();
-    // if (StrUtil.isNotEmpty(this.videoUrl) && this.videoUrl.toLowerCase().endsWith('.wma')) {
-    //   ToastUtil.showToast('wma格式不支持拖动快进。')
-    //   return
-    // }
+    this.stopProgressTask();
+    if (StrUtil.isNotEmpty(this.videoUrl) && this.videoUrl.toLowerCase().endsWith('.wma')) {
+      ToastUtil.showToast('wma格式不支持拖动快进。')
+      return
+    }
 
     this.mIjkMediaPlayer.seekTo(value);
     // this.startPlayOrResumePlay()