Explorar o código

Merge remote-tracking branch 'origin/master' into feature/桌面卡片-抽离ijkplayer播放控制

# Conflicts:
#	entry/src/main/ets/controller/AvSessionController.ets
#	entry/src/main/ets/entryability/EntryAbility.ets
#	entry/src/main/ets/view/LocalMusic.ets
chendeben hai 1 ano
pai
achega
94836ca371

+ 2 - 2
AppScope/app.json5

@@ -2,8 +2,8 @@
   "app": {
     "bundleName": "com.xgplayer.ttmusic.hm",
     "vendor": "example",
-    "versionCode": 20250806,
-    "versionName": "1.5.0",
+    "versionCode": 20250810,
+    "versionName": "1.5.1",
     "icon": "$media:app_icon",
     "label": "$string:app_name",
     "multiAppMode": {

+ 4 - 0
entry/src/main/ets/common/constants/CommonConstants.ets

@@ -15,6 +15,10 @@
 
 import { VideoSpeed } from '../../viewmodel/VideoSpeed';
 
+export const STR_LOCK_VIDEO: string = '.私密音频';
+export const STR_FAC_VIDEO: string = '.我的收藏';
+export const STR_HISTORY_MUSIC: string = '.最近播放';
+export const VIP_FILEPATH: string = '.vv'
 /**
  * Common constants for all features.
  */

+ 2 - 3
entry/src/main/ets/common/util/UserUtil.ets

@@ -1,13 +1,12 @@
 import { cryptoFramework } from "@kit.CryptoArchitectureKit";
 import { LogUtil, PreferencesUtil, StrUtil, ToastUtil } from "@pura/harmony-utils";
-import { CommonConstants } from "../constants/CommonConstants";
+import { CommonConstants, STR_LOCK_VIDEO, VIP_FILEPATH } from "../constants/CommonConstants";
 import { http } from "@kit.NetworkKit";
 import { bundleManager } from "@kit.AbilityKit";
 import { BusinessError } from '@kit.BasicServicesKit';
 import { hilog } from "@kit.PerformanceAnalysisKit";
 import { FileUtil } from "@pura/harmony-utils";
 import { fileUri, picker } from "@kit.CoreFileKit";
-import { LocalMusic } from "../../view/LocalMusic";
 import { VipPage } from "../../pages/VipPage";
 
 // 用户相关接口定义
@@ -430,7 +429,7 @@ export default class UserUtil {
             const documentViewPicker = new picker.DocumentViewPicker()
             let documentSaveResult = await documentViewPicker.save({ pickerMode: picker.DocumentPickerMode.DOWNLOAD })
             let download_path = new fileUri.FileUri(documentSaveResult[0]).path + '/'
-            let filePath = download_path + LocalMusic.STR_LOCK_VIDEO+ '/' + VipPage.VIP_FILEPATH
+            let filePath = download_path + STR_LOCK_VIDEO+ '/' + VIP_FILEPATH
             if (FileUtil.accessSync(filePath)) {
               FileUtil.unlink(filePath);
             }

+ 177 - 77
entry/src/main/ets/common/util/Utility.ets

@@ -17,12 +17,11 @@ import { dataSharePredicates, uniformTypeDescriptor } from '@kit.ArkData';
 import { systemShare } from '@kit.ShareKit';
 import { fileUri, picker } from '@kit.CoreFileKit';
 import {  common, UIAbility, Want } from '@kit.AbilityKit';
-import { CommonConstants } from '../constants/CommonConstants';
+import { CommonConstants, STR_LOCK_VIDEO, VIP_FILEPATH } from '../constants/CommonConstants';
 import { window } from '@kit.ArkUI';
 import { bundleManager } from '@kit.AbilityKit'
 import { pinyin4js } from '@ohos/pinyin4js';
 import { VipData } from '../../viewmodel/VipData';
-import { LocalMusic } from '../../view/LocalMusic';
 import { VipPage } from '../../pages/VipPage';
 import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
 import ImageUtils from './ImageUtils';
@@ -408,8 +407,9 @@ export class Utility {
     return false;
   }
 
+
   // 获取缩略图
-  static async getFetchFrameByTime(filePath: string) {
+  static async getFetchFrameByTime(filePath: string,time?:number) {
     if(Utility.isMusicByExtension(filePath)){
       return undefined
     }
@@ -422,6 +422,10 @@ export class Utility {
       avImageGenerator.fdSrc = avFileDescriptor;
       // 初始化入参
       let timeUs = 0
+      console.info('onecold time='+time)
+      if(time){
+        timeUs = (time > 0) ? time*60 : 0
+      }
       let queryOption = media.AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC
       let param: media.PixelMapParams = {
         width : 300,
@@ -813,7 +817,7 @@ export class Utility {
           let videoItem:VideoItem = new VideoItem('',inputPath,inputPath,type,0,'')
           const metadata: FFprobeMetadata = JSON.parse(outputJson);
           const format = metadata.format;
-
+          console.log(`onecold outputJson:${outputJson}`);
           let file = fs.openSync(inputPath, fs.OpenMode.READ_ONLY | fs.OpenMode.CREATE)
           console.info('readMetaInfoFFmpeg asset file.path: ', file.path);
           videoItem = new VideoItem(file.name,inputPath,inputPath,type,0,'')
@@ -860,6 +864,7 @@ export class Utility {
             if (!name) {
               name = getFileNameWithoutExtension(inputPath);
             }
+            let pixelMap:image.PixelMap|undefined|null = undefined
             // Create VideoItem
             videoItem = new VideoItem(
               name,
@@ -900,13 +905,22 @@ export class Utility {
             try {
               if (hasCover) {
                   //提取封面
-                  await getFFmpegCover(inputPath, imagePath);
-                  imagePath = fileUri.getUriFromPath(imagePath)
-                  videoItem.pixelMapPath  = imagePath;
+                  let isSuccess:boolean= await getFFmpegCover(inputPath, imagePath);
+
+                  if(isSuccess){
+                    imagePath = fileUri.getUriFromPath(imagePath)
+                  }else{
+                    imagePath = ''
+                  }
+                videoItem.pixelMapPath  = imagePath;
               }else if(Utility.isVideoByExtension(inputPath)){
-                //提取封面
-                await getVideoFFmpegCover(inputPath, imagePath);
-                videoItem.mimeType = getFileFormatByPath(inputPath)
+                pixelMap = await Utility.getFetchFrameByTime(inputPath)
+                if(pixelMap!==undefined&&pixelMap!==null){
+                  imagePath = await ImageUtil.savePixelMap(pixelMap,context.filesDir,md5Name)
+                  imagePath = fileUri.getUriFromPath(imagePath)
+                }else{
+                  imagePath = ''
+                }
                 imagePath = fileUri.getUriFromPath(imagePath)
                 videoItem.pixelMapPath  = imagePath;
               }
@@ -1215,20 +1229,8 @@ export class Utility {
   }
 
 
-  static getFileDirName(filePath: string,rootPath:string): string{
-    if(filePath===rootPath){
-      return '首页'
-    }
-    let result = FileUtil.getFileName(filePath)
-    if(StrUtil.isEmpty(result))
-      return ''
 
 
-    if(result.startsWith('.'))
-      result = result.replace(/\./g, '')
-    return result
-  }
-
 
 
 
@@ -1359,7 +1361,7 @@ export class Utility {
       const documentViewPicker = new picker.DocumentViewPicker()
       let documentSaveResult = await documentViewPicker.save({ pickerMode: picker.DocumentPickerMode.DOWNLOAD })
       let download_path = new fileUri.FileUri(documentSaveResult[0]).path + '/'
-      let filePath = download_path + LocalMusic.STR_LOCK_VIDEO+ '/' + VipPage.VIP_FILEPATH
+      let filePath = download_path + STR_LOCK_VIDEO+ '/' + VIP_FILEPATH
       let expireDate =  Utility.readDataFromFile(filePath)
 
       return expireDate;
@@ -1511,57 +1513,122 @@ function formatDuration(seconds: string, forceHHMMSS: boolean = false): string {
 }
 
 
-// 定义解析结果的数据结构
+
+/**
+ * 定义解析结果的数据结构
+ */
 class MusicInfo {
   artist: string = "";  // 艺术家名称
-  title: string = "";  // 歌曲名称
+  title: string = "";   // 歌曲名称
   isValid: boolean = false;  // 格式是否有效
 }
+
 /**
- * 解析音乐文件名
- * @param fileName - 待解析的文件名(需包含扩展名)
- * @returns 包含解析结果的MusicInfo对象
+ * 精简版常见中文姓氏(音乐场景优化版)
  */
+const COMMON_CHINESE_SURNAMES = [
+// 超常见姓氏(覆盖约80%人口)
+  '李','王','张','刘','陈','杨','黄','赵','吴','周',
+  '徐','孙','马','朱','胡','林','郭','何','高','罗',
+  '郑','梁','谢','宋','唐','许','韩','邓','曹','彭',
+  '曾','萧','田','董','潘','袁','于','蔡','余','杜',
+
+  // 音乐行业常见姓氏(歌手高频姓)
+  '汪','苏','薛','谭','华','金','魏','陶','姜','窦',
+  '章','毛','易','方','宋','任','沈','贾','江','孔',
+
+  // 组合/乐队常见字
+  '羽','泉','信','乐','花','飞','龙','风','云','草'
+];
+
 /**
- * 智能解析音乐文件名(支持多种分隔符和前缀序号)
+ * 歌手特征关键词(优化版)
+ */
+const ARTIST_KEYWORDS = [
+// 中文特征
+  '乐队','组合','乐团','和','&','合唱',' featuring',
+  // 英文特征
+  'feat','ft','with','vs','presents','presents',
+  // 符号特征
+  '×','X','※','◆','♫'
+];
+/**
+ * 智能解析音乐文件名(支持多种分隔符和格式)
  * @param fileName - 待解析的完整文件名
  * @returns 结构化音乐信息
  */
+/**
+ * 智能解析音乐文件名
+ */
 function parseMusicFileName(fileName: string): MusicInfo {
-  const result = new MusicInfo();
-
-  // 1. 预处理:移除首尾空格(保留中间空格)
-  const cleanName = fileName.trim();
+  const result: MusicInfo = new MusicInfo();
+  if (!fileName) return result;
 
-  // 2. 提取文件扩展名(以最后一个点分隔)
-  const lastDotIndex = cleanName.lastIndexOf('.');
-  if (lastDotIndex < 0) return result; // 无扩展名
+  // 预处理
+  const cleanName: string = fileName.trim();
+  const lastDotIndex: number = cleanName.lastIndexOf('.');
+  const baseName: string = lastDotIndex > 0 ?
+  cleanName.substring(0,  lastDotIndex).trim() :
+    cleanName;
 
-  const baseName = cleanName.substring(0,  lastDotIndex).trim();
-  const extension = cleanName.substring(lastDotIndex  + 1);
+  // 支持的分隔符(明确定义类型)
+  const separators: string[] = ['-', '-', '—', '~', '~'];
 
-  // 3. 支持多种分隔符(中英文短横线)
-  const separators = ['-', '-', '—']; // 半角/全角短横线
-  let dashIndex = -1;
+  // 寻找分隔位置
+  let bestSplitIndex: number = -1;
 
-  // 查找最后一个有效分隔符位置
   for (const sep of separators) {
-    const index = baseName.lastIndexOf(sep);
-    if (index > dashIndex) dashIndex = index;
+    const index: number = baseName.lastIndexOf(sep);
+    if (index > bestSplitIndex) {
+      bestSplitIndex = index;
+    }
   }
 
-  // 4. 核心解析逻辑
-  if (dashIndex > 0 && dashIndex < baseName.length  - 1) {
-    let artistPart = baseName.substring(0,  dashIndex).trim();
-    result.title  = baseName.substring(dashIndex  + 1).trim();
+  // 分割字符串
+  if (bestSplitIndex > 0 && bestSplitIndex < baseName.length  - 1) {
+    let part1: string = baseName.substring(0,  bestSplitIndex).trim();
+    let part2: string = baseName.substring(bestSplitIndex  + 1).trim();
+
+    // 处理前缀序号
+    part1 = part1.replace(/^\d+[\s\.\-- —~~]*/, '').trim();
+
+    // 判断歌手部分(明确定义返回类型)
+    const identifyArtist = (str: string): boolean => {
+      return COMMON_CHINESE_SURNAMES.some((surname:  string) =>
+      str.startsWith(surname)  ||
+      new RegExp(`[ ,,、&&]${surname}`).test(str)
+      ) || ARTIST_KEYWORDS.some((keyword:  string) =>
+      str.includes(keyword)
+      );
+    };
+
+    // 判断歌手位置
+    const part1IsArtist: boolean = identifyArtist(part1);
+    const part2IsArtist: boolean = identifyArtist(part2);
 
-    // 5. 处理前缀序号(如"04 - ")
-    const numPrefixRegex = /^\d+\s*[--—]\s*/; // 匹配数字+分隔符组合
-    artistPart = artistPart.replace(numPrefixRegex,  '').trim();
+    if (part1IsArtist && !part2IsArtist) {
+      result.artist  = part1;
+      result.title  = part2;
+    } else if (part2IsArtist && !part1IsArtist) {
+      result.artist  = part2;
+      result.title  = part1;
+    } else {
+      result.artist  = part1.length  <= part2.length  ? part1 : part2;
+      result.title  = part1.length  <= part2.length  ? part2 : part1;
+    }
 
-    // 6. 最终有效性验证
-    result.artist  = artistPart;
-    result.isValid  = (result.artist.length  > 0 && result.title.length  > 0);
+    // 后处理
+    result.title  = result.title
+      .replace(/(?:\(|()[^))]*(?:)|\))/g, '')
+      .replace(/\s*[—-]\s*(?:Live|Version|Remix|伴奏).*/i, '')
+      .trim();
+
+    // 有效性验证
+    result.isValid  = result.artist.length  > 0 &&
+      result.title.length  > 0;
+  } else {
+    result.title  = baseName;
+    result.isValid  = result.title.length  > 0;
   }
 
   return result;
@@ -1579,37 +1646,70 @@ function getFileNameWithoutExtension(filePath: string): string {
  * @param inputPath 音乐文件路径
  * @returns Promise<void>
  */
-async function getFFmpegCover(inputPath: string, outputPath: string) {
+// async function getFFmpegCover(inputPath: string, outputPath: string) : Promise<boolean>{
+//   let commands = ["ffmpeg", "-y","-i", inputPath, "-map", "0:v", "-c:v", "copy", outputPath];
+//   FFmpeg.execute(commands, {
+//     logCallback: (logLevel: number, logMessage: string) => {
+//       console.info(`[FFmpeg LOG] [${logLevel}]${logMessage}`)
+//     },
+//     progressCallback: (message: string) => {
+//       console.info(`[FFmpeg progress]${JSON.stringify(FFProgressMessageParser.parse(message))}`)
+//     },
+//   })
+//     .then(() => {
+//       return true
+//       console.info("FFmpeg execution succeeded.");
+//     })
+//     .catch((error: Error) => {
+//       return false
+//       console.error(`FFmpeg execution failed with error: ${error.message}`);
+//     });
+//   return ;
+// }
+
+/**
+ * 从音乐文件中提取封面
+ * @param inputPath 音乐文件路径
+ * @param outputPath 封面输出路径
+ * @returns Promise<boolean> 表示操作是否成功
+ */
+async function getFFmpegCover(inputPath: string, outputPath: string): Promise<boolean> {
   let commands = ["ffmpeg", "-y","-i", inputPath, "-map", "0:v", "-c:v", "copy", outputPath];
-  FFmpeg.execute(commands, {
-    logCallback: (logLevel: number, logMessage: string) => {
-      console.info(`[FFmpeg LOG] [${logLevel}]${logMessage}`)
-    },
-    progressCallback: (message: string) => {
-      console.info(`[FFmpeg progress]${JSON.stringify(FFProgressMessageParser.parse(message))}`)
-    },
-  })
-    .then(() => {
-      console.info("FFmpeg execution succeeded.");
-    })
-    .catch((error: Error) => {
-      console.error(`FFmpeg execution failed with error: ${error.message}`);
+
+  try {
+    await FFmpeg.execute(commands,  {
+      logCallback: (logLevel: number, logMessage: string) => {
+        console.info(`[FFmpeg  LOG] [${logLevel}] ${logMessage}`);
+      },
+      progressCallback: (message: string) => {
+        console.info(`[FFmpeg  progress] ${JSON.stringify(FFProgressMessageParser.parse(message))}`);
+      },
     });
+    console.info("FFmpeg  execution succeeded.");
+    return true;
+  } catch (error) {
+    console.error(`FFmpeg  execution failed with error: ${error instanceof Error ? error.message  : String(error)}`);
+    return false;
+  }
 }
 /**
  * 从视频文件中提取封面 提前视频第5帧的封面
  * @param inputPath 文件路径
  * @returns Promise<void>
  */
-async function getVideoFFmpegCover(inputPath: string, outputPath: string,duration?:string) {
-  let durationText = "00:00:05"
-  if(duration&&Number(duration) >300000){//如果时间超过5分钟,截10s的缩略图
-    durationText = "00:00:10"
-  }
-  if(duration&&Number(duration) <5000){//如果时间小5s,截0s的缩略图
-    durationText = "00:00:01"
-  }
-  let commands = ["ffmpeg", "-y","-i", inputPath, "-ss", "00:00:03", "-t", "1","-r",'1','-q:v','2','-f','image2', outputPath];
+async function getVideoFFmpegCover(inputPath: string, outputPath: string) {
+
+  // 更可靠的命令,专门提取视频第一帧作为封面
+  const commands = [
+    "ffmpeg",
+    "-y",                  // 覆盖输出文件(如果存在)
+    "-i", inputPath,       // 输入文件
+    "-ss", "00:00:00",     // 定位到开始位置
+    "-vframes", "1",       // 只提取1帧
+    "-q:v", "2",           // 高质量JPEG(1-31,2是最高质量)
+    "-f", "image2",        // 强制输出为图像格式
+    outputPath
+  ];
   FFmpeg.execute(commands, {
     logCallback: (logLevel: number, logMessage: string) => {
       console.info(`[FFmpegX LOG] [${logLevel}]${logMessage}`)

+ 9 - 4
entry/src/main/ets/controller/AvSessionController.ets

@@ -20,6 +20,7 @@ import { avSession } from '@kit.AVSessionKit';
 import { BackgroundTaskManager } from '../common/util/BackgroundTaskManager';
 import { VideoItem } from '../viewmodel/VideoItem';
 import { ImageUtil, PreferencesUtil, StrUtil } from '@pura/harmony-utils';
+import { image } from '@kit.ImageKit';
 import { SettingPage } from '../pages/SettingPage';
 import LyricUtil from '../common/util/LyricUtil';
 import ImageUtils from '../common/util/ImageUtils';
@@ -136,10 +137,14 @@ export class AvSessionController {
       BackgroundTaskManager.startContinuousTask(this.context);
 
       let pixelMapPath: string | undefined = curSource.pixelMapPath
-      let imagePixMap: PixelMap | string = await ImageUtil.getPixelMapFromMedia($r('app.media.ic_avatar7'));
-      if (pixelMapPath && StrUtil.isNotEmpty(pixelMapPath)) {
-        if (pixelMapPath.startsWith('http')) {
-          imagePixMap = pixelMapPath
+
+      let imagePixMap: PixelMap|string ;
+      if(pixelMapPath&&StrUtil.isNotEmpty(pixelMapPath)){
+        if(pixelMapPath.startsWith('http')){
+          imagePixMap =pixelMapPath
+        }else{
+          imagePixMap = await ImageUtil.imagePathToPixelMap(pixelMapPath)
+        }
         } else {
           imagePixMap = await ImageUtils.imagePathToPixelMap(pixelMapPath)
         }

+ 116 - 97
entry/src/main/ets/entryability/EntryAbility.ets

@@ -22,7 +22,8 @@ import { systemShare } from '@kit.ShareKit';
 import { SpiderMan } from '@simplepeng/spider-man';
 import { CustomCrashHandler } from '../common/utils/CustomCrashHandler';
 import { smartMobilityCommon } from '@kit.CarKit';
-
+import { url } from '@kit.ArkTS';
+import { display } from '@kit.ArkUI';
 
 
 /**
@@ -104,7 +105,8 @@ class MyParcelable implements rpc.Parcelable {
 export default class EntryAbility extends UIAbility {
     // UI上下文对象,用于获取窗口信息
     private uiContext?: UIContext;
-    private awareness: smartMobilityCommon.SmartMobilityAwareness = smartMobilityCommon.getSmartMobilityAwareness();
+    // private awareness: smartMobilityCommon.SmartMobilityAwareness = smartMobilityCommon.getSmartMobilityAwareness();
+    private awareness: smartMobilityCommon.SmartMobilityAwareness|undefined = canIUse("SystemCapability.SmartOptimizer.SmartMobility")?smartMobilityCommon.getSmartMobilityAwareness():undefined;
 
     /**
      * 窗口尺寸变化回调函数
@@ -154,10 +156,10 @@ export default class EntryAbility extends UIAbility {
         AppUtil.init(this.context);
         AppStorage.setOrCreate('context', this.context);
         hilog.info(0x0000, 'Heanup2', '%{public}s', 'Ability onCreate');
-        
+
         // 注册卡片call事件监听器
         this.registerWidgetCallListeners();
-        
+
         // 异步初始化统一播放器服务,避免阻塞生命周期
         this.initializePlayerServiceAsync();
 
@@ -175,7 +177,7 @@ export default class EntryAbility extends UIAbility {
     async onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
         hilog.info(0x0000, 'Heanup2', `onNewWant, want=${JSON.stringify(want)}`);
         super.onNewWant(want, launchParam);
-        
+
         // 异步处理Want参数,避免阻塞生命周期
         this.handleWantAsync(want);
         this.handleWeChatCallIfNeed(want)
@@ -237,7 +239,7 @@ export default class EntryAbility extends UIAbility {
 
     onDestroy() {
         hilog.info(0x0000, 'Heanup2', '%{public}s', 'Ability onDestroy');
-        
+
         // 关键修复:在APP销毁时保存播放器状态
         try {
             const unifiedPlayerService = UnifiedPlayerService.getInstance();
@@ -249,27 +251,31 @@ export default class EntryAbility extends UIAbility {
         } catch (error) {
             hilog.error(0x0000, 'Heanup2', `Failed to release UnifiedPlayerService: ${error}`);
         }
-        
+
         // 注销卡片call事件监听器
         this.unregisterWidgetCallListeners();
-        
-        let types: smartMobilityCommon.SmartMobilityType[] = [smartMobilityCommon.SmartMobilityType.CAR_HOP];
-        // 出行连接状态回调函数
-        const callBack = (info: smartMobilityCommon.SmartMobilityInfo) => {
-            hilog.info(0x0000, 'Received smart mobility info: ', JSON.stringify(info));
-        };
-        // 解注册智慧出行连接状态的监听 示例2
-        this.awareness.off('smartMobilityStatus', types, callBack);
-    }
+
+        hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
+        if(this.awareness){
+            let types: smartMobilityCommon.SmartMobilityType[] = [smartMobilityCommon.SmartMobilityType.CAR_HOP];
+            // 出行连接状态回调函数
+            const callBack = (info: smartMobilityCommon.SmartMobilityInfo) => {
+                hilog.info(0x0000, 'Received smart mobility info: ', JSON.stringify(info));
+            };
+            // 解注册智慧出行连接状态的监听 示例2
+            this.awareness.off('smartMobilityStatus', types, callBack);
+        }
+        }
+
 
     onWindowStageCreate(windowStage: window.WindowStage) {
         // Main window is created, set main page for this ability
         hilog.info(0x0000, 'Heanup2', '%{public}s', 'Ability onWindowStageCreate');
-        
+
         // 使用自定义崩溃处理器替代 SpiderMan.init()
         // SpiderMan.init();
         CustomCrashHandler.init();
-        
+
         AppUtil.init(this.context);
 
         //1.获取应用主窗口。
@@ -280,6 +286,14 @@ export default class EntryAbility extends UIAbility {
             // LogUtil.info( 'getMainWindow = ');
             GlobalContext.getContext().setObject('windowClass', data); // 使用GlobalContext替代globalThis
 
+            let curDisplay = display.getDisplayByIdSync(windowClass.getWindowProperties().displayId);
+            console.info('twocold curDisplay = '+curDisplay.name);
+            if(curDisplay.name =='HiCar'||curDisplay.name =='SuperLauncher'){
+                AppStorage.setOrCreate('curDisplayIsHiCar', true);
+            }else{
+                AppStorage.setOrCreate('curDisplayIsHiCar', false);
+            }
+
             windowClass.setWindowLayoutFullScreen(true).then(() => {
                 console.info('Succeeded in setting the window layout to full-screen mode.');
             }).catch((e: BusinessError) => {
@@ -349,55 +363,60 @@ export default class EntryAbility extends UIAbility {
     onForeground() {
         // Ability has brought to foreground
         hilog.info(0x0000, 'Heanup2', '%{public}s', 'Ability onForeground start');
-        
+
         // 异步处理前台逻辑,避免阻塞生命周期
         setTimeout(() => {
             this.handleForegroundAsync();
         }, 10);
-        
+
         hilog.info(0x0000, 'Heanup2', '%{public}s', 'Ability onForeground end');
     }
 
     onBackground() {
         // Ability has back to background
         hilog.info(0x0000, 'Heanup2', '%{public}s', 'Ability onBackground start');
-        
+
         // 异步处理后台逻辑,避免阻塞生命周期
         setTimeout(() => {
             this.handleBackgroundAsync();
         }, 10);
-        
+
         hilog.info(0x0000, 'Heanup2', '%{public}s', 'Ability onBackground end');
     }
 
 
     getHiCarStatus(){
-        this.awareness = smartMobilityCommon.getSmartMobilityAwareness();
+        if(this.awareness){
+            // this.awareness = smartMobilityCommon.getSmartMobilityAwareness();
 
-        // 业务类型
-        let types: smartMobilityCommon.SmartMobilityType[] = [smartMobilityCommon.SmartMobilityType.HICAR];
-        // 获取出行业务连接状态
-        let info = this.awareness.getSmartMobilityStatus(types[0]);
+            // 业务类型
+            let types: smartMobilityCommon.SmartMobilityType[] = [smartMobilityCommon.SmartMobilityType.HICAR];
+            // 获取出行业务连接状态
+            let info = this.awareness.getSmartMobilityStatus(types[0]);
 
-        hilog.info(0x0000, 'getHiCarStatus  info: ', JSON.stringify(info));
-        if(info&&info.status==1){
-            AppStorage.setOrCreate('isHiCarStatus', true);
-        }else{
-            AppStorage.setOrCreate('isHiCarStatus', false);
-        }
-
-        // 出行连接状态回调函数
-        const callBack = (info: smartMobilityCommon.SmartMobilityInfo) => {
-            hilog.info(0x0000, 'getHiCarStatus Received smart mobility info: ', JSON.stringify(info));
+            hilog.info(0x0000, 'getHiCarStatus  info: ', JSON.stringify(info));
             if(info&&info.status==1){
                 AppStorage.setOrCreate('isHiCarStatus', true);
             }else{
                 AppStorage.setOrCreate('isHiCarStatus', false);
             }
-            this.sendChangeEvent()
-        };
-        // 注册智慧出行连接状态的监听
-        this.awareness.on('smartMobilityStatus', types, callBack);
+
+            // 出行连接状态回调函数
+            const callBack = (info: smartMobilityCommon.SmartMobilityInfo) => {
+                hilog.info(0x0000, 'getHiCarStatus Received smart mobility info: ', JSON.stringify(info));
+                if(info&&info.status==1){
+                    AppStorage.setOrCreate('isHiCarStatus', true);
+                }else{
+                    AppStorage.setOrCreate('isHiCarStatus', false);
+                }
+                this.sendChangeEvent()
+            };
+            // 注册智慧出行连接状态的监听
+            this.awareness.on('smartMobilityStatus', types, callBack);
+        }else{
+            AppStorage.setOrCreate('isHiCarStatus', false);
+        }
+
 
 
     }
@@ -419,12 +438,12 @@ export default class EntryAbility extends UIAbility {
                 try {
                     const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
                     hilog.info(0x0000, 'Heanup2', `🎵 Widget call: playPause received`);
-                    
+
                     // 异步发送播放/暂停事件到主应用(包含服务就绪检查)
                     this.sendWidgetControlEvent('PLAY_PAUSE', params).catch((error: Error) => {
                         hilog.error(0x0000, 'Heanup2', `❌ playPause async error: ${error}`);
                     });
-                    
+
                     return new MyParcelable(1, 'playPause_success');
                 } catch (error) {
                     hilog.error(0x0000, 'Heanup2', `❌ playPause handler error: ${error}`);
@@ -438,12 +457,12 @@ export default class EntryAbility extends UIAbility {
                     hilog.info(0x0000, 'Heanup2', `🎵 Widget call: nextSong received`);
                     const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
                     hilog.info(0x0000, 'Heanup2', `Widget nextSong params: ${JSON.stringify(params)}`);
-                    
+
                     // 异步发送下一首事件到主应用(包含服务就绪检查)
                     this.sendWidgetControlEvent('NEXT_SONG', params).catch((error: Error) => {
                         hilog.error(0x0000, 'Heanup2', `❌ nextSong async error: ${error}`);
                     });
-                    
+
                     return new MyParcelable(2, 'nextSong_success');
                 } catch (error) {
                     hilog.error(0x0000, 'Heanup2', `❌ nextSong handler error: ${error}`);
@@ -457,12 +476,12 @@ export default class EntryAbility extends UIAbility {
                     hilog.info(0x0000, 'Heanup2', `🎵 Widget call: prevSong received`);
                     const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
                     hilog.info(0x0000, 'Heanup2', `Widget prevSong params: ${JSON.stringify(params)}`);
-                    
+
                     // 异步发送上一首事件到主应用(包含服务就绪检查)
                     this.sendWidgetControlEvent('PREV_SONG', params).catch((error: Error) => {
                         hilog.error(0x0000, 'Heanup2', `❌ prevSong async error: ${error}`);
                     });
-                    
+
                     return new MyParcelable(3, 'prevSong_success');
                 } catch (error) {
                     hilog.error(0x0000, 'Heanup2', `❌ prevSong handler error: ${error}`);
@@ -476,12 +495,12 @@ export default class EntryAbility extends UIAbility {
                     hilog.info(0x0000, 'Heanup2', `🎵 Widget call: openApp received`);
                     const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
                     hilog.info(0x0000, 'Heanup2', `Widget openApp params: ${JSON.stringify(params)}`);
-                    
+
                     // 异步发送打开应用事件到主应用
                     this.sendWidgetControlEvent('OPEN_APP', params).catch((error: Error) => {
                         hilog.error(0x0000, 'Heanup2', `❌ openApp async error: ${error}`);
                     });
-                    
+
                     return new MyParcelable(4, 'openApp_success');
                 } catch (error) {
                     hilog.error(0x0000, 'Heanup2', `❌ openApp handler error: ${error}`);
@@ -520,29 +539,29 @@ export default class EntryAbility extends UIAbility {
     private async sendWidgetControlEvent(command: string, params: Record<string, Object>): Promise<void> {
         try {
             hilog.info(0x0000, 'Heanup2', `🎵 收到桌面卡片指令: ${command}`);
-            
+
             // 获取UnifiedPlayerService实例
             const unifiedService = UnifiedPlayerService.getInstance();
-            
+
             // 确保服务已经初始化
             if (!unifiedService) {
                 hilog.error(0x0000, 'Heanup2', '❌ UnifiedPlayerService 未初始化');
                 return;
             }
-            
+
             // 获取详细的服务就绪状态信息
             const readinessInfo = unifiedService.getServiceReadinessInfo();
             hilog.info(0x0000, 'Heanup2', `🔍 Service readiness: ${JSON.stringify(readinessInfo.details)}`);
-            
+
             // 智能等待服务完全就绪(多级检查)
             hilog.info(0x0000, 'Heanup2', '🔄 检查服务就绪状态...');
             let ready = await this.waitForServiceReady(unifiedService);
-            
+
             if (ready) {
                 hilog.info(0x0000, 'Heanup2', '✅ UnifiedPlayerService 已就绪');
             } else {
                 hilog.warn(0x0000, 'Heanup2', '⚠️ UnifiedPlayerService 未完全就绪,尝试执行基础操作');
-                
+
                 // 检查是否至少可以进行基础操作
                 if (!unifiedService.isAllServicesReady()) {
                     const finalReadinessInfo = unifiedService.getServiceReadinessInfo();
@@ -550,19 +569,19 @@ export default class EntryAbility extends UIAbility {
                     return;
                 }
             }
-            
+
             // 检查初始化状态
             const currentState = unifiedService.getCurrentState();
             const playlist = unifiedService.getPlaylist();
             hilog.info(0x0000, 'Heanup2', `🎵 Widget command - Service state: isPlaying=${currentState.isPlaying}, currentIndex=${currentState.currentIndex}, playlistSize=${playlist.length}`);
-            
+
             // 对于播放控制操作,检查是否有可播放内容
             if ((command === 'PLAY_PAUSE' || command === 'NEXT_SONG' || command === 'PREV_SONG') && playlist.length === 0) {
                 hilog.warn(0x0000, 'Heanup2', '⚠️ 播放列表为空,尝试强制数据恢复');
-                
+
                 // 强制触发数据恢复
                 await this.forceDataRestoration(unifiedService);
-                
+
                 // 重新检查播放列表
                 const updatedPlaylist = unifiedService.getPlaylist();
                 if (updatedPlaylist.length === 0) {
@@ -571,20 +590,20 @@ export default class EntryAbility extends UIAbility {
                 }
                 hilog.info(0x0000, 'Heanup2', `✅ 强制数据恢复完成,播放列表大小: ${updatedPlaylist.length}`);
             }
-            
+
             // 根据命令执行相应的播放控制
             switch (command) {
                 case 'PLAY_PAUSE':
                     // 获取卡片传递的当前显示状态
                     const widgetIsPlaying = params['widgetIsPlaying'] as boolean;
                     const hasWidgetState = widgetIsPlaying !== undefined;
-                    
+
                     // 获取服务内部状态
                     const latestState = unifiedService.getCurrentState();
-                    
+
                     hilog.info(0x0000, 'Heanup2', `🎵 卡片显示状态: isPlaying=${widgetIsPlaying} (有效: ${hasWidgetState})`);
                     hilog.info(0x0000, 'Heanup2', `🎵 服务内部状态: isPlaying=${latestState.isPlaying}`);
-                    
+
                     if (hasWidgetState) {
                         // 新逻辑:根据卡片显示的状态来决定操作
                         // - 如果卡片显示播放按钮(widgetIsPlaying=false),则执行播放操作
@@ -612,31 +631,31 @@ export default class EntryAbility extends UIAbility {
                         }
                     }
                     break;
-                    
+
                 case 'NEXT_SONG':
                     await unifiedService.playNext();
                     hilog.info(0x0000, 'Heanup2', '✅ Widget command: Next song');
                     break;
-                    
+
                 case 'PREV_SONG':
                     await unifiedService.playPrevious();
                     hilog.info(0x0000, 'Heanup2', '✅ Widget command: Previous song');
                     break;
-                    
+
                 case 'OPEN_APP':
                     hilog.info(0x0000, 'Heanup2', '✅ Widget command: Open app (handled by UI)');
                     break;
-                    
+
                 default:
                     hilog.warn(0x0000, 'Heanup2', `❓ Unknown widget command: ${command}`);
                     break;
             }
-            
+
             // 操作完成后,广播最新状态给主应用UI和桌面卡片
             setTimeout(() => {
                 this.broadcastCurrentPlayerState();
                 hilog.info(0x0000, 'Heanup2', '📡 桌面卡片操作后广播状态更新');
-                
+
                 // 同时触发UnifiedPlayerService的状态广播,确保所有监听器都能收到更新
                 try {
                     unifiedService.broadcastCurrentState();
@@ -644,9 +663,9 @@ export default class EntryAbility extends UIAbility {
                     hilog.error(0x0000, 'Heanup2', `❌ 触发服务状态广播失败: ${error}`);
                 }
             }, 100); // 短延迟,确保操作完全完成
-            
+
             hilog.info(0x0000, 'Heanup2', `✅ Widget control command processed: ${command}`);
-            
+
         } catch (error) {
             hilog.error(0x0000, 'Heanup2', `❌ Failed to process widget control command: ${error}`);
         }
@@ -658,16 +677,16 @@ export default class EntryAbility extends UIAbility {
     private async forceDataRestoration(unifiedService: UnifiedPlayerService): Promise<void> {
         try {
             hilog.info(0x0000, 'Heanup2', '🔄 开始强制数据恢复...');
-            
+
             // 检查是否已经有数据恢复完成
             if (unifiedService.isDataRestorationCompleted()) {
                 hilog.info(0x0000, 'Heanup2', '✅ 数据已恢复,无需强制恢复');
                 return;
             }
-            
+
             // 调用UnifiedPlayerService的强制数据恢复方法
             const restored = await unifiedService.forceDataRestoration();
-            
+
             if (restored) {
                 hilog.info(0x0000, 'Heanup2', '✅ 强制数据恢复成功');
             } else {
@@ -675,10 +694,10 @@ export default class EntryAbility extends UIAbility {
                 // 如果强制恢复失败,再等待一段时间
                 await unifiedService.waitForDataRestoration(2000);
             }
-            
+
             // 额外等待一小段时间,确保播放列表数据完全加载
             await new Promise<void>(resolve => setTimeout(resolve, 300));
-            
+
         } catch (error) {
             hilog.error(0x0000, 'Heanup2', `❌ 强制数据恢复失败: ${error}`);
         }
@@ -690,27 +709,27 @@ export default class EntryAbility extends UIAbility {
     private async waitForServiceReady(unifiedService: UnifiedPlayerService): Promise<boolean> {
         try {
             hilog.info(0x0000, 'Heanup2', '🔄 开始等待UnifiedPlayerService完全就绪');
-            
+
             // 检查是否是刚启动的应用(数据还没恢复)
             const currentPlaylist = unifiedService.getPlaylist();
             const isJustStarted = currentPlaylist.length === 0;
             const isDataRestored = unifiedService.isDataRestorationCompleted();
-            
+
             if (isJustStarted || !isDataRestored) {
                 hilog.info(0x0000, 'Heanup2', `🔄 检测到冷启动状态 - 播放列表为空: ${isJustStarted}, 数据未恢复: ${!isDataRestored}`);
-                
+
                 // 对于冷启动,给予更长的等待时间,并强制检查数据恢复
                 const isReady = await unifiedService.waitForAllServicesReady(10000, true);
-                
+
                 if (isReady) {
                     hilog.info(0x0000, 'Heanup2', '✅ UnifiedPlayerService完全就绪(冷启动)');
                     return true;
                 }
-                
+
                 // 如果仍未就绪,尝试强制触发数据恢复
                 hilog.warn(0x0000, 'Heanup2', '⚠️ 冷启动等待超时,尝试强制数据恢复');
                 await this.forceDataRestoration(unifiedService);
-                
+
                 // 再次检查服务状态
                 const retryReady = await unifiedService.waitForAllServicesReady(3000, false);
                 if (retryReady) {
@@ -720,28 +739,28 @@ export default class EntryAbility extends UIAbility {
             } else {
                 // 对于已经有数据的情况,使用正常的等待时间
                 const isReady = await unifiedService.waitForAllServicesReady(5000, true);
-                
+
                 if (isReady) {
                     hilog.info(0x0000, 'Heanup2', '✅ UnifiedPlayerService完全就绪');
                     return true;
                 }
             }
-            
+
             // 如果主要服务就绪但数据未恢复,再做一次宽松检查
             hilog.info(0x0000, 'Heanup2', '⚠️ 主要检查超时,进行备用检查');
             const basicReady = await unifiedService.waitForAllServicesReady(2000, false);
-            
+
             if (basicReady) {
                 // 检查是否至少有播放数据
                 const currentState = unifiedService.getCurrentState();
                 const playlist = unifiedService.getPlaylist();
-                
+
                 if (playlist.length > 0 || currentState.currentIndex >= 0) {
                     hilog.info(0x0000, 'Heanup2', `✅ 基础服务就绪,播放列表: ${playlist.length} 首歌曲`);
                     return true;
                 }
             }
-            
+
             hilog.warn(0x0000, 'Heanup2', '⚠️ Service readiness check timeout, proceeding with limited functionality');
             return false;
         } catch (error) {
@@ -760,14 +779,14 @@ export default class EntryAbility extends UIAbility {
                 hilog.info(0x0000, 'Heanup2', '🔄 开始异步初始化 UnifiedPlayerService');
                 await UnifiedPlayerService.getInstance().initialize(this.context);
                 hilog.info(0x0000, 'Heanup2', '✅ UnifiedPlayerService 异步初始化成功');
-                
+
                 // 对于冷启动场景,立即尝试数据恢复
                 const unifiedService = UnifiedPlayerService.getInstance();
                 if (!unifiedService.isDataRestorationCompleted()) {
                     hilog.info(0x0000, 'Heanup2', '🔄 冷启动检测到数据未恢复,开始预恢复');
                     await unifiedService.forceDataRestoration();
                 }
-                
+
                 // 延迟广播当前状态,确保卡片能接收到初始状态
                 setTimeout(() => {
                     this.broadcastCurrentPlayerState();
@@ -784,14 +803,14 @@ export default class EntryAbility extends UIAbility {
     private handleForegroundAsync(): void {
         try {
             hilog.info(0x0000, 'Heanup2', '🔄 处理前台逻辑');
-            
+
             // 确保播放器服务可用
             const playerService = UnifiedPlayerService.getInstance();
             if (playerService) {
                 // 广播当前状态给卡片
                 this.broadcastCurrentPlayerState();
             }
-            
+
             hilog.info(0x0000, 'Heanup2', '✅ 前台逻辑处理完成');
         } catch (error) {
             hilog.error(0x0000, 'Heanup2', `❌ 前台逻辑处理失败: ${error}`);
@@ -804,10 +823,10 @@ export default class EntryAbility extends UIAbility {
     private handleBackgroundAsync(): void {
         try {
             hilog.info(0x0000, 'Heanup2', '🔄 处理后台逻辑');
-            
+
             // 后台时可以执行一些清理或保存操作
             // 但要确保不会阻塞生命周期
-            
+
             hilog.info(0x0000, 'Heanup2', '✅ 后台逻辑处理完成');
         } catch (error) {
             hilog.error(0x0000, 'Heanup2', `❌ 后台逻辑处理失败: ${error}`);
@@ -853,7 +872,7 @@ export default class EntryAbility extends UIAbility {
             const currentSong = unifiedService.getCurrentSong();
             const playlist = unifiedService.getPlaylist();
             const currentIndex = unifiedService.getCurrentIndex();
-            
+
             if (currentSong) {
                 // 构建播放器状态广播数据
                 const broadcastData: BroadcastData = {
@@ -884,18 +903,18 @@ export default class EntryAbility extends UIAbility {
                         totalCount: playlist.length
                     } as PlaylistBroadcast
                 };
-                
+
                 // 发送状态变化事件
                 const publishInfo: PublishInfo = {
                     data: JSON.stringify(broadcastData)
                 };
-                
+
                 // 使用emitter发送事件
                 const eventData: EventDataWrapper = {
                     data: broadcastData
                 };
                 emitter.emit({ eventId: 1001 }, eventData); // 使用特定的事件ID
-                
+
                 hilog.info(0x0000, 'Heanup2', `📡 Broadcasted current player state: ${currentSong.name}, isPlaying=${currentState.isPlaying}`);
             } else {
                 hilog.info(0x0000, 'Heanup2', '📡 No current song to broadcast');

+ 152 - 146
entry/src/main/ets/pages/NewIndex.ets

@@ -1,6 +1,6 @@
 import { AppUtil, DeviceUtil, DisplayUtil, LogUtil, PreferencesUtil, ToastUtil } from '@pura/harmony-utils';
 import TitleBar from '../view/TitleBar';
-import { router } from '@kit.ArkUI';
+import { curves, router } from '@kit.ArkUI';
 import mainViewModel, { MainViewModel } from '../viewmodel/MainViewModel';
 import ItemData from '../viewmodel/ItemData';
 import type { sysResource } from '../viewmodel/ItemData';
@@ -16,7 +16,6 @@ import { common } from '@kit.AbilityKit';
 import { PrintBiddingTokenUtils } from '../common/util/PrintBiddintTokenUtils';
 import { ArrayList } from '@kit.ArkTS';
 import { CSJUtil } from '../common/util/CSJUtil';
-import { LocalMusic } from '../view/LocalMusic';
 import { StreamContent } from '../view/StreamContent';
 import { AvSessionController } from '../controller/AvSessionController';
 import { BreakpointSystem, BreakpointTypeEnum } from '../common/util/BreakpointSystem';
@@ -38,6 +37,8 @@ import { smartMobilityCommon } from '@kit.CarKit';
 import { UpdateLogManager } from '../common/util/UpdateLogManager';
 import OnlineUpdateLogDialog from '../dialog/OnlineUpdateLog';
 import OnlineUpdateLog from '../dialog/OnlineUpdateLog';
+import { LocalMusic } from '../view/LocalMusic';
+
 // import { FFmpeg, FFProgressMessageParser } from '@sj/ffmpeg';
 
 const TAG = 'NewIndex'; // 日志标签
@@ -281,7 +282,7 @@ struct NewIndex {
       Column() {
         this.getLeftView()
       }
-
+      .backgroundColor(Color.Transparent)
       Column() {
         Stack() {
           // 本地音乐内容区
@@ -302,7 +303,10 @@ struct NewIndex {
             Column()
               .width('100%')
               .height('100%')
-              .backgroundColor(Color.Transparent)
+              .backgroundBlurStyle(BlurStyle.BACKGROUND_THIN)
+              .transition(TransitionEffect.OPACITY.animation({
+                curve: curves.springMotion(0, 1)
+              }))
               .onClick(() => {
                 // 点击遮罩层关闭抽屉
                 this.getUIContext()?.animateTo({ duration: 555 }, () => {
@@ -394,8 +398,6 @@ struct NewIndex {
     Column() {
       Column() {
 
-        this.buildUserInfoCard()
-
         this.getDrawerView()
       }
       .width('100%')
@@ -565,152 +567,156 @@ struct NewIndex {
    */
   @Builder
   getDrawerView() {
+
     List({ space: 0, scroller: this.scroller }) {
-      ForEach(this.isHiCar()||!this.isShowTitleBar?mainViewModel.getDrawerData():mainViewModel.getDrawerData2(), (item: ItemData, index: number) => {
-        ListItem() {
-          Button({ type: ButtonType.Capsule, stateEffect: true }) {
-            Row() {
-              // 菜单图标
-              if (typeof item.img === 'object' && item.img !== null && (item.img as sysResource).type === 'symbol') {
-                SymbolGlyph((item.img as sysResource).value as Resource)// .size({ width: 22, height: 22 })
-                  .fontSize(22)
-                  .fontColor([this.themeColor])
-                  .alignSelf(ItemAlign.Center)
-                  .margin({ left: 25 })
-              } else {
-                Image(item.img as Resource)
+      ListItemGroup({ header: this.buildUserInfoCard() }) {
+        ForEach(this.isHiCar()||!this.isShowTitleBar?mainViewModel.getDrawerData():mainViewModel.getDrawerData2(), (item: ItemData, index: number) => {
+          ListItem() {
+            Button({ type: ButtonType.Capsule, stateEffect: true }) {
+              Row() {
+                // 菜单图标
+                if (typeof item.img === 'object' && item.img !== null && (item.img as sysResource).type === 'symbol') {
+                  SymbolGlyph((item.img as sysResource).value as Resource)// .size({ width: 22, height: 22 })
+                    .fontSize(22)
+                    .fontColor([this.themeColor])
+                    .alignSelf(ItemAlign.Center)
+                    .margin({ left: 25 })
+                } else {
+                  Image(item.img as Resource)
+                    .height(22)
+                    .alignSelf(ItemAlign.Center)
+                    .fillColor(this.themeColor)
+                    .margin({ left: 25 })
+                }
+
+                // 菜单标题
+                Text(item.title)
+                  .margin({ left: 10, right: 20 })
+                  .fontSize(15)
+                  .fontColor(this.isTextSelected(index) ? this.themeColor : $r('app.color.index_tab_font_color'))
+                  .fontWeight(480)
+                Blank()
+                // 右侧箭头
+                Image($r('app.media.arrow_right'))
+                  .width(22)
                   .height(22)
-                  .alignSelf(ItemAlign.Center)
-                  .fillColor(this.themeColor)
-                  .margin({ left: 25 })
+                  .margin({ left: 20, right: 0 })
+                  .align(Alignment.Center)
               }
-
-              // 菜单标题
-              Text(item.title)
-                .margin({ left: 10, right: 20 })
-                .fontSize(15)
-                .fontColor(this.isTextSelected(index) ? this.themeColor : $r('app.color.index_tab_font_color'))
-                .fontWeight(480)
-              Blank()
-              // 右侧箭头
-              Image($r('app.media.arrow_right'))
-                .width(22)
-                .height(22)
-                .margin({ left: 20, right: 0 })
-                .align(Alignment.Center)
+              .width('100%')
+              .height(55)
             }
-            .width('100%')
-            .height(55)
-          }
-          .backgroundColor(Color.Transparent)
-          .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
-          .onClick(async () => {
-            // 根据 item.id 跳转或切换功能
-            switch (item.id) {
-              case MainViewModel.MENU_MUSIC:
-                this.mType = 0
-                this.modeType = 0
-                this.doShowDrawer()
-                break
-              case MainViewModel.MENU_FILE_SCAN:
-                this.mType = 2
-                this.doShowDrawer()
-                break
-            // case MainViewModel.MENU_HOME:
-            //   this.mType = 0
-            //   this.modeType = 0
-            //   this.getUIContext()?.animateTo({ duration: 555 }, () => {
-            //     this.isShowDrawer = false
-            //   })
-            //   break
-              case MainViewModel.MENU_MIEDIA_KU:
-                this.modeType = 1
-                this.mType = 0
-                this.doShowDrawer()
-                break
-              case MainViewModel.MENU_MIEDIA_ARTIST:
-                this.modeType = 2
-                this.mType = 0
-                this.doShowDrawer()
-                break
-              case MainViewModel.MENU_MIEDIA_ALBUM:
-                this.modeType = 3
-                this.mType = 0
-                this.doShowDrawer()
-                break
-              case MainViewModel.MENU_SETTING:
-                this.mType = 3
-                this.doShowDrawer()
-
-                break
-              case MainViewModel.MENU_VIP:
-                router.pushUrl({
-                  url: 'pages/VipPage'
-                }, router.RouterMode.Single);
-                break
-              case MainViewModel.MENU_USER:
-                this.mType = 1
-                this.doShowDrawer()
-                break
-              case MainViewModel.MENU_NET_CONNECT:
-                this.mType = 1
-                this.doShowDrawer()
-                break
-              case MainViewModel.MENU_SIMI:
-                router.pushUrl({
-                  url: 'pages/VerifyPage'
-                });
-                break
-
-              case MainViewModel.MENU_DUTY:
-                router.pushUrl({
-                  url: 'pages/WebIndex',
-                  params: { titleName: '用户协议', webUrl: CommonConstants.NEW_DUTY }
-                });
-                break
-              case MainViewModel.MENU_HAOPING:
-                Utility.gotoMarket(getContext(this) as common.UIAbilityContext, this.bundleName)
-                break
-
-              case MainViewModel.MENU_ABOUT:
-                this.mType = 4
-                this.doShowDrawer()
-                break
-              case MainViewModel.MENU_UPDATE:
-                AlertDialog.show({
-                  title: '版本更新',
-                  message: '当前版本为最新版本:' + this.versionName,
-                  autoCancel: true,
-                  alignment: DialogAlignment.Center,
-                  offset: { dx: 0, dy: -20 }, //在Y轴方向上的编译量
-                  confirm: {
-                    value: '确定',
-                    fontColor: Color.White,
-                    backgroundColor: $r('app.color.title_bar_bg'),
-                    action: () => {
+            .backgroundColor(Color.Transparent)
+            .clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
+            .onClick(async () => {
+              // 根据 item.id 跳转或切换功能
+              switch (item.id) {
+                case MainViewModel.MENU_MUSIC:
+                  this.mType = 0
+                  this.modeType = 0
+                  this.doShowDrawer()
+                  break
+                case MainViewModel.MENU_FILE_SCAN:
+                  this.mType = 2
+                  this.doShowDrawer()
+                  break
+                // case MainViewModel.MENU_HOME:
+                //   this.mType = 0
+                //   this.modeType = 0
+                //   this.getUIContext()?.animateTo({ duration: 555 }, () => {
+                //     this.isShowDrawer = false
+                //   })
+                //   break
+                case MainViewModel.MENU_MIEDIA_KU:
+                  this.modeType = 1
+                  this.mType = 0
+                  this.doShowDrawer()
+                  break
+                case MainViewModel.MENU_MIEDIA_ARTIST:
+                  this.modeType = 2
+                  this.mType = 0
+                  this.doShowDrawer()
+                  break
+                case MainViewModel.MENU_MIEDIA_ALBUM:
+                  this.modeType = 3
+                  this.mType = 0
+                  this.doShowDrawer()
+                  break
+                case MainViewModel.MENU_SETTING:
+                  this.mType = 3
+                  this.doShowDrawer()
+
+                  break
+                case MainViewModel.MENU_VIP:
+                  router.pushUrl({
+                    url: 'pages/VipPage'
+                  }, router.RouterMode.Single);
+                  break
+                case MainViewModel.MENU_USER:
+                  this.mType = 1
+                  this.doShowDrawer()
+                  break
+                case MainViewModel.MENU_NET_CONNECT:
+                  this.mType = 1
+                  this.doShowDrawer()
+                  break
+                case MainViewModel.MENU_SIMI:
+                  router.pushUrl({
+                    url: 'pages/VerifyPage'
+                  });
+                  break
+
+                case MainViewModel.MENU_DUTY:
+                  router.pushUrl({
+                    url: 'pages/WebIndex',
+                    params: { titleName: '用户协议', webUrl: CommonConstants.NEW_DUTY }
+                  });
+                  break
+                case MainViewModel.MENU_HAOPING:
+                  Utility.gotoMarket(getContext(this) as common.UIAbilityContext, this.bundleName)
+                  break
+
+                case MainViewModel.MENU_ABOUT:
+                  this.mType = 4
+                  this.doShowDrawer()
+                  break
+                case MainViewModel.MENU_UPDATE:
+                  AlertDialog.show({
+                    title: '版本更新',
+                    message: '当前版本为最新版本:' + this.versionName,
+                    autoCancel: true,
+                    alignment: DialogAlignment.Center,
+                    offset: { dx: 0, dy: -20 }, //在Y轴方向上的编译量
+                    confirm: {
+                      value: '确定',
+                      fontColor: Color.White,
+                      backgroundColor: $r('app.color.title_bar_bg'),
+                      action: () => {
+                      }
                     }
-                  }
-                })
-                break
-              case MainViewModel.MENU_YSZC:
-                router.pushUrl({
-                  url: 'pages/WebIndex',
-                  params: { titleName: '隐私政策', webUrl: CommonConstants.NEW_YS_HW }
-                });
-                break
-
-              case MainViewModel.MENU_SHARE:
-                this.gotoShare()
-                break
-            }
-          })
-        }
-        .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
-          .animation({ duration: 600, curve: Curve.Ease, delay: 60 * index }))
-        .width('90%')
-        .height(55)
+                  })
+                  break
+                case MainViewModel.MENU_YSZC:
+                  router.pushUrl({
+                    url: 'pages/WebIndex',
+                    params: { titleName: '隐私政策', webUrl: CommonConstants.NEW_YS_HW }
+                  });
+                  break
+
+                case MainViewModel.MENU_SHARE:
+                  this.gotoShare()
+                  break
+              }
+            })
+          }
+          .transition(TransitionEffect.move(TransitionEdge.BOTTOM)
+            .animation({ duration: 600, curve: Curve.Ease, delay: 60 * index }))
+          .width('90%')
+          .height(55)
+
+        })
+      }
 
-      })
     }
     .width('86%')
     .borderRadius(20)

+ 2 - 3
entry/src/main/ets/pages/ScanFilePage.ets

@@ -4,12 +4,11 @@ import lottie from '@ohos/lottie'
 import { AnimationItem } from '@ohos/lottie'
 import { taskpool } from '@kit.ArkTS'
 import { fileUri, picker } from '@kit.CoreFileKit'
-import { LocalMusic } from '../view/LocalMusic'
 import { Utility } from '../common/util/Utility'
 import MediaTable from '../common/util/MediaTable'
 import { VideoItem } from '../viewmodel/VideoItem'
 import Logger from '../common/util/Logger'
-import { CommonConstants } from '../common/constants/CommonConstants'
+import { CommonConstants, STR_LOCK_VIDEO } from '../common/constants/CommonConstants'
 import { emitter } from '@kit.BasicServicesKit'
 import { BreakpointTypeEnum } from '../common/util/BreakpointSystem'
 import { ConfigurationConstant, Context } from '@kit.AbilityKit'
@@ -81,7 +80,7 @@ export struct ScanFilePage{
     let documentSaveResult = await documentViewPicker.save({ pickerMode: picker.DocumentPickerMode.DOWNLOAD })
     this.rootPath = new fileUri.FileUri(documentSaveResult[0]).path
 
-    this.lockPath =  this.rootPath +'/'+ LocalMusic.STR_LOCK_VIDEO
+    this.lockPath =  this.rootPath +'/'+ STR_LOCK_VIDEO
     LogUtil.info('onecold 文件扫描 aboutToAppear')
     this.initLottie(this.path,true)
     setTimeout(()=>{

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

@@ -38,7 +38,7 @@ export struct SettingPage {
   static readonly IS_CUSTOMIZE_BG: string = 'is_customize_bg';
   static readonly IS_GRID_MUSIC: string = 'is_grid_music';
   static readonly IS_CUSTOMIZE_BG_PATH: string = 'is_customize_bg_path';
-  static readonly IS_SCROLL_HIDE: string = 'isScrollHide';
+  static readonly IS_SCROLL_HIDE: string = 'isAutoScrollHide';
   static readonly IS_SAMETIME_PLAY: string = 'isSameTimePlay';
   static readonly CUSTOMIZE_BG_BLUR: string = 'customize_bg_blur';
   static readonly BG_BRIGHTNESS: string = 'bg_brightness';
@@ -674,10 +674,11 @@ export struct SettingPage {
                 .height(30);
             }
             .height(55)
+            .visibility(Visibility.None)
             .clickEffect({ level: ClickEffectLevel.HEAVY })
 
             Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
-
+              .visibility(Visibility.None)
             // 网格布局
             Row() {
               Text('网格模式')

+ 2 - 3
entry/src/main/ets/pages/SplashIndex.ets

@@ -3,7 +3,7 @@ import { common } from '@kit.AbilityKit'
 import data_preferences from '@ohos.data.preferences'
 import Logger from '../common/util/Logger'
 import { router, window } from '@kit.ArkUI'
-import { CommonConstants } from '../common/constants/CommonConstants'
+import { CommonConstants, STR_LOCK_VIDEO, VIP_FILEPATH } from '../common/constants/CommonConstants'
 import { CSJUtil } from '../common/util/CSJUtil'
 import { ConfigManager } from '../common/util/ConfigManager'
 // import { AdSlotBuilder, CSJAdCreator, CSJAdSdk, CSJSplashAd,
@@ -16,7 +16,6 @@ import { PrintBiddingTokenUtils } from '../common/util/PrintBiddintTokenUtils'
 import { AppUtil, Base64Util, FileUtil, LogUtil, StrUtil, ToastUtil } from '@pura/harmony-utils'
 import { Utility } from '../common/util/Utility'
 import { VideoItem } from '../viewmodel/VideoItem'
-import { LocalMusic } from '../view/LocalMusic'
 import { fileIo, fileUri, picker, ReadTextOptions } from '@kit.CoreFileKit'
 import { VipPage } from './VipPage'
 import { VipData } from '../viewmodel/VipData'
@@ -141,7 +140,7 @@ struct  SplashIndex{
     let documentSaveResult = await documentViewPicker.save({ pickerMode: picker.DocumentPickerMode.DOWNLOAD })
     let download_path = new fileUri.FileUri(documentSaveResult[0]).path
     this.rootPath = download_path
-    let vPath = download_path + '/'+ LocalMusic.STR_LOCK_VIDEO+ '/' + VipPage.VIP_FILEPATH
+    let vPath = download_path + '/'+ STR_LOCK_VIDEO+ '/' + VIP_FILEPATH
     this.expireDate =  this.readDataFromFile(vPath)
     Utility.setNoble(this.expireDate)
     this.doMain()

+ 1 - 1
entry/src/main/ets/pages/UserCenter.ets

@@ -437,7 +437,7 @@ export struct UserCenter {
       Scroll() {
         Column() {
           this.buildUserInfoCard()
-          if(!Utility.isNoble()||!Utility.isForever()){
+          if(!Utility.isForever()){
             this.buildVipPlans()
           }
           this.buildVipFeatures()

+ 3 - 4
entry/src/main/ets/pages/VipPage.ets

@@ -1,7 +1,7 @@
 import { promptAction, router, window } from '@kit.ArkUI';
 import TitleBar from '../view/TitleBar';
 import { webview } from '@kit.ArkWeb';
-import { CommonConstants } from '../common/constants/CommonConstants';
+import { CommonConstants, STR_LOCK_VIDEO, VIP_FILEPATH } from '../common/constants/CommonConstants';
 import { AES,
   AppUtil,
   Base64Util,
@@ -19,7 +19,6 @@ import { Utility } from '../common/util/Utility';
 import fileIo, { ReadTextOptions } from '@ohos.file.fs';
 import { fileUri, picker } from '@kit.CoreFileKit';
 import { VipData } from '../viewmodel/VipData';
-import { LocalMusic } from '../view/LocalMusic';
 import { DialogHelper } from '@pura/harmony-dialog';
 import { common, ConfigurationConstant } from '@kit.AbilityKit';
 import * as wxOpenSdk from '@tencent/wechat_open_sdk';
@@ -67,7 +66,7 @@ export  struct  VipPage{
 
 
 
-  static readonly VIP_FILEPATH: string = '.vv'
+  // static readonly VIP_FILEPATH: string = '.vv'
 
   @State appName:string = ''
   @State expireDate:string = ''
@@ -104,7 +103,7 @@ export  struct  VipPage{
     const documentViewPicker = new picker.DocumentViewPicker()
     let documentSaveResult = await documentViewPicker.save({ pickerMode: picker.DocumentPickerMode.DOWNLOAD })
     let download_path = new fileUri.FileUri(documentSaveResult[0]).path + '/'
-    this.filePath = download_path + LocalMusic.STR_LOCK_VIDEO+ '/' + VipPage.VIP_FILEPATH
+    this.filePath = download_path + STR_LOCK_VIDEO+ '/' + VIP_FILEPATH
 
     this.expireDate =  this.readDataFromFile(this.filePath)
     this.bundleName = await  AppUtil.getBundleName()

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 344 - 259
entry/src/main/ets/view/LocalMusic.ets


+ 5 - 1
entry/src/main/resources/base/element/string.json

@@ -454,7 +454,7 @@
     },
     {
       "name": "sync_tips",
-      "value": "如果你在系统的文件管理删除了音乐媒体文件,可以使用校正数据来删除校正对于的数据库。"
+      "value": "如果你在系统的文件管理删除了音乐媒体文件,可以使用校正数据来删除校正对应的数据。"
     },
     {
       "name": "sync_dataing",
@@ -467,6 +467,10 @@
     {
       "name": "lossless",
       "value": "Lossless"
+    },
+    {
+      "name": "select_all",
+      "value": "多选"
     }
   ]
 }

+ 5 - 23
oh-package-lock.json5

@@ -11,21 +11,19 @@
     "@chinalike/popup@^0.0.7": "@chinalike/popup@0.0.7",
     "@keke/color-picker@^1.0.4": "@keke/color-picker@1.0.4",
     "@ohos/juniversalchardet@^2.0.2": "@ohos/juniversalchardet@2.0.2",
-    "@ohos/lottie@^2.0.19": "@ohos/lottie@2.0.19",
+    "@ohos/lottie@^2.0.23": "@ohos/lottie@2.0.23",
     "@ohos/pinyin4js@^2.0.2": "@ohos/pinyin4js@2.0.2",
     "@ohos/pulltorefresh@^2.1.1": "@ohos/pulltorefresh@2.1.1",
     "@pura/harmony-dialog@^1.0.6": "@pura/harmony-dialog@1.0.6",
     "@pura/harmony-utils@^1.2.4": "@pura/harmony-utils@1.2.4",
     "@pura/spinkit@^1.0.4": "@pura/spinkit@1.0.4",
     "@seagazer/cclyric@lib": "@seagazer/cclyric@lib",
-    "@sgaolei/lrc_parser@^1.0.0": "@sgaolei/lrc_parser@1.0.0",
     "@simplepeng/spider-man@^1.0.1": "@simplepeng/spider-man@1.0.1",
     "@sj/ffmpeg@^1.2.5": "@sj/ffmpeg@1.2.5",
     "@taobao-ohos/utdid_sdk@oh_modules/.ohpm/@cashier_alipay+cashiersdk@15.8.32/oh_modules/@cashier_alipay/cashiersdk/lib/utdid_sdk-1.0.9.har": "@taobao-ohos/utdid_sdk@oh_modules/.ohpm/@cashier_alipay+cashiersdk@15.8.32/oh_modules/@cashier_alipay/cashiersdk/lib/utdid_sdk-1.0.9.har",
     "class-transformer@^0.5.1": "class-transformer@0.5.1",
     "libblueshield.so@oh_modules/.ohpm/@alipay+blueshieldsdk@cow+0gass5tzqhymzjby2f6sd+g2xtoatqsiyvi9qsy=/oh_modules/@alipay/blueshieldsdk/src/main/cpp/types/libblueshield": "libblueshield.so@oh_modules/.ohpm/@alipay+blueshieldsdk@cow+0gass5tzqhymzjby2f6sd+g2xtoatqsiyvi9qsy=/oh_modules/@alipay/blueshieldsdk/src/main/cpp/types/libblueshield",
     "libffmpeg.so@oh_modules/.ohpm/@sj+ffmpeg@1.2.5/oh_modules/@sj/ffmpeg/src/main/cpp/types/libffmpeg": "libffmpeg.so@oh_modules/.ohpm/@sj+ffmpeg@1.2.5/oh_modules/@sj/ffmpeg/src/main/cpp/types/libffmpeg",
-    "liblrcparser.so@oh_modules/.ohpm/@sgaolei+lrc_parser@1.0.0/oh_modules/@sgaolei/lrc_parser/src/main/cpp/types/liblrcparser": "liblrcparser.so@oh_modules/.ohpm/@sgaolei+lrc_parser@1.0.0/oh_modules/@sgaolei/lrc_parser/src/main/cpp/types/liblrcparser",
     "pako@^2.1.0": "pako@2.1.0"
   },
   "packages": {
@@ -78,11 +76,11 @@
       "resolved": "https://repo.harmonyos.com/ohpm/@ohos/juniversalchardet/-/juniversalchardet-2.0.2.har",
       "registryType": "ohpm"
     },
-    "@ohos/lottie@2.0.19": {
+    "@ohos/lottie@2.0.23": {
       "name": "@ohos/lottie",
-      "version": "2.0.19",
-      "integrity": "sha512-6YGMeuHCdJ4PrJGPj3ycJob61TYbEcgk4GugbcuTGS6eJHJpDUuTja9lPhDmD6RL/etm3i2j4IVd+QCpfI6TWA==",
-      "resolved": "https://repo.harmonyos.com/ohpm/@ohos/lottie/-/lottie-2.0.19.har",
+      "version": "2.0.23",
+      "integrity": "sha512-SiVmsyllXLcvSrh98qsXtjSoVaxy1bm8sGZzmz94DZOjdQ2zSCnTzY627EevkCCFxAA5HIbqFPUWokhc8bp/3Q==",
+      "resolved": "https://repo.harmonyos.com/ohpm/@ohos/lottie/-/lottie-2.0.23.har",
       "registryType": "ohpm"
     },
     "@ohos/pinyin4js@2.0.2": {
@@ -132,16 +130,6 @@
       "resolved": "lib",
       "registryType": "local"
     },
-    "@sgaolei/lrc_parser@1.0.0": {
-      "name": "@sgaolei/lrc_parser",
-      "version": "1.0.0",
-      "integrity": "sha512-JnkArqOidM0qN+Daoxka+PvMq4Eqyv39/oZ/4vHTYlU5ssCTUqJ0GWF7ti9ZQTrDbg89Shx6hjODVXhs3IjgAg==",
-      "resolved": "https://repo.harmonyos.com/ohpm/@sgaolei/lrc_parser/-/lrc_parser-1.0.0.har",
-      "registryType": "ohpm",
-      "dependencies": {
-        "liblrcparser.so": "file:./src/main/cpp/types/liblrcparser"
-      }
-    },
     "@simplepeng/spider-man@1.0.1": {
       "name": "@simplepeng/spider-man",
       "version": "1.0.1",
@@ -185,12 +173,6 @@
       "resolved": "oh_modules/.ohpm/@sj+ffmpeg@1.2.5/oh_modules/@sj/ffmpeg/src/main/cpp/types/libffmpeg",
       "registryType": "local"
     },
-    "liblrcparser.so@oh_modules/.ohpm/@sgaolei+lrc_parser@1.0.0/oh_modules/@sgaolei/lrc_parser/src/main/cpp/types/liblrcparser": {
-      "name": "liblrcparser.so",
-      "version": "1.0.0",
-      "resolved": "oh_modules/.ohpm/@sgaolei+lrc_parser@1.0.0/oh_modules/@sgaolei/lrc_parser/src/main/cpp/types/liblrcparser",
-      "registryType": "local"
-    },
     "pako@2.1.0": {
       "name": "pako",
       "version": "2.1.0",

+ 2 - 3
oh-package.json5

@@ -14,18 +14,17 @@
   "dependencies": {
     "@pura/harmony-utils": "^1.2.4",
     "@pura/harmony-dialog": "^1.0.6",
-    "@ohos/lottie": "^2.0.19",
     "@ohos/pulltorefresh": "^2.1.1",
     "@chinalike/popup": "^0.0.7",
     "@seagazer/cclyric": "file:./lib",
     "@changwei/chardet": "^1.0.0",
     "@cashier_alipay/cashiersdk": "^15.8.32",
     "@keke/color-picker": "^1.0.4",
-    "@sgaolei/lrc_parser": "^1.0.0",
     "@ohos/pinyin4js": "^2.0.2",
     "@simplepeng/spider-man": "^1.0.1",
     "@ohos/juniversalchardet": "^2.0.2",
-    "@sj/ffmpeg": "^1.2.5"
+    "@sj/ffmpeg": "^1.2.5",
+    "@ohos/lottie": "^2.0.23"
   },
   "dynamicDependencies": {}
 }

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio