Browse Source

优化缓存管理

onecold 4 tháng trước cách đây
mục cha
commit
6898cb48c0

+ 17 - 17
entry/src/main/ets/common/util/CacheManageService.ets

@@ -86,36 +86,36 @@ class CacheManageService {
   private buildCacheRules(): Array<CacheRule> {
     const context: common.Context = this.ensureContext()
     const filesDir: string = this.normalizePath(context.filesDir ?? '')
-    const cacheDir: string = this.normalizePath(context.cacheDir ?? '')
+    const cacheDir: string = this.normalizePath(context.filesDir ?? '')
     const rules: Array<CacheRule> = []
 
-    if (filesDir.length > 0) {
+    if (cacheDir.length > 0) {
       rules.push({
-        key: 'remote_cache',
-        title: '网盘',
-        path: this.buildPath(filesDir, 'remote_cache'),
+        key: 'system_cache',
+        title: '系统',
+        path: cacheDir,
         removable: true,
-        description: '远程歌曲缓存',
-        clearStrategy: 'self'
+        description: '应用临时缓存',
+        clearStrategy: 'children'
       })
+    }
+
+    if (filesDir.length > 0) {
       rules.push({
         key: 'remote_thumbs',
-        title: '缩略图',
+        title: '图',
         path: this.buildPath(filesDir, 'remote_thumbs'),
         removable: true,
-        description: '网盘列表缩略图缓存',
+        description: '网盘列表图缓存',
         clearStrategy: 'self'
       })
-    }
-
-    if (cacheDir.length > 0) {
       rules.push({
-        key: 'system_cache',
-        title: '系统',
-        path: cacheDir,
+        key: 'remote_cache',
+        title: '网盘',
+        path: this.buildPath(filesDir, 'remote_cache'),
         removable: true,
-        description: '应用临时缓存',
-        clearStrategy: 'children'
+        description: '远程歌曲缓存',
+        clearStrategy: 'self'
       })
     }
 

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

@@ -2155,7 +2155,7 @@ export struct SettingPage {
               title: { title: '缓存管理' }
             })
 
-            
+            Line().width('100%').height(0.3).backgroundColor($r('app.color.index_tab_unselected_font_color'))
             // WebDAV上传默认重复文件处理方式
             Row() {
               SymbolGlyph($r('sys.symbol.doc'))

+ 5 - 5
entry/src/main/ets/view/CacheManageSheet.ets

@@ -436,7 +436,7 @@ export struct CacheManageSheet {
     }
     .enabled(!this.isRootPath() && !this.isLoading)
     .padding({ left: 10, right: 12 })
-    .attributeModifier(new ButtonFancyModifier(86, 40))
+    .attributeModifier(new ButtonFancyModifier(70, 35))
     .attributeModifier(new ShadowModifier())
     .opacity(!this.isRootPath() && !this.isLoading ? 1 : 0.55)
     .onClick(() => {
@@ -655,13 +655,13 @@ export struct CacheManageSheet {
       Row() {
         Row({ space: 6 }) {
           this.selectToggleButtonBuilder()
-          Text(`已选 ${Utility.formatFileSize(this.getSelectedSize())}`)
-            .fontSize(12)
-            .fontColor(Color.Gray)
           Text(`已选 ${this.selectedPaths.length} 项`)
-            .fontSize(13)
+            .fontSize(12)
             .fontWeight(600)
             .fontColor($r('app.color.text_color'))
+          Text(`${Utility.formatFileSize(this.getSelectedSize())}`)
+            .fontSize(12)
+            .fontColor($r('app.color.text_color'))
         }
         .layoutWeight(1)
 

+ 164 - 84
entry/src/main/ets/view/LocalMusic.ets

@@ -373,6 +373,9 @@ export struct LocalMusic {
   @State bluetoothLyricEnabled: boolean = false // 蓝牙歌词开关
   @State bluetoothLyricMode: number = 0 // 蓝牙歌词显示模式
   private lastBluetoothLyricLine: string = '' // 上一次发送的歌词行
+  private audioRoutingManager?: audio.AudioRoutingManager
+  private outputDeviceChangeCallback?: Callback<audio.DeviceChangeAction>
+  private audioInterruptResumePending: boolean = false
   @State showFindLocation: boolean = true
   @StorageProp("EnablePointLight") enablePointLight: boolean = true
   @State showSimple: boolean = true//开启沉浸模式
@@ -1170,95 +1173,27 @@ export struct LocalMusic {
       this.doChangeSetting()
     });
 
-    let event: Callback<InterruptEvent> = (event) => {
-      // LogUtils.getInstance().LOGI(`onecold 与其他应用音频冲突 被截断点event: ${JSON.stringify(event)}`);
-      console.info(`heanup onecold 与其他应用音频冲突 被截断点event: ${JSON.stringify(event)}`)
-      this.savePlaybackPosition()
-      if (event.hintType === InterruptHintType.INTERRUPT_HINT_PAUSE) {
-        this.pause();
-      } else if (event.hintType === InterruptHintType.INTERRUPT_HINT_RESUME) {
-        console.info(`heanup onecold audioInterrupt startPlayOrResumePlay: ${JSON.stringify(event)}`)
-        this.startPlayOrResumePlay();
-      } else if (event.hintType === InterruptHintType.INTERRUPT_HINT_STOP) {
-        console.info(`heanup onecold 与其他应用音频冲突 被截断点 INTERRUPT_HINT_STOP`)
-        if(!this.isSameTimePlay){
-          this.stop();
-        }
-      }
-
-    }
-    this.mIjkMediaPlayer.on('audioInterrupt', event);
-    // 音频设备断连回调处理比如蓝牙耳机断开
-    let deviceChangeEvent: Callback<InterruptEvent> = (event) => {
-      console.info(`onecold Heanup deviceChange event: ${JSON.stringify(event)}`);
-      if (event.reason === DeviceChangeReason.REASON_NEW_DEVICE_AVAILABLE) { // 音频设备连接
-
-      } else if (event.reason === DeviceChangeReason.REASON_OLD_DEVICE_UNAVAILABLE) { // 音频设备断开连接
-        console.info('heanup onecold 音频设备断开连接, 暂停播放')
-        this.pause();
-      }
-    }
-    // 订阅音频设备断开和连接事件
-    this.mIjkMediaPlayer.on('deviceChange', deviceChangeEvent);
+    this.mIjkMediaPlayer.on('audioInterrupt', (event: InterruptEvent) => {
+      this.handleAudioInterrupt(event)
+    });
+    this.mIjkMediaPlayer.on('deviceChange', (event: InterruptEvent) => {
+      this.handlePlayerDeviceChange(event)
+    });
 
     const eventPause: emitter.InnerEvent = { eventId: EventConstants.EVENT_PLAY_PAUSE }
     // 监听广播事件(hicar断开暂停播放)
     emitter.on(eventPause, (eventData: emitter.EventData) => {
       console.info('heanup onecold hicar断开暂停播放, 暂停播放')
+      this.audioInterruptResumePending = false
       this.pause();
 
     });
     let audioManager = audio.getAudioManager();  // 需要先创建AudioManager实例。
-    let audioRoutingManager = audioManager.getRoutingManager();  // 再调用AudioManager的方法创建AudioRoutingManager实例。
-    // 监听蓝牙音响断开音频设备状态变化。
-    audioRoutingManager.on('deviceChange', audio.DeviceFlag.OUTPUT_DEVICES_FLAG, (deviceChanged: audio.DeviceChangeAction) => {
-      console.info(`onecold device change type : ${deviceChanged.type}`);  // 设备连接状态变化,0为连接,1为断开连接。
-      console.info(`onecold device descriptor size : ${deviceChanged.deviceDescriptors.length}`);
-      console.info(`onecold device change descriptor : ${deviceChanged.deviceDescriptors[0].deviceRole}`);  // 设备角色。
-      console.info(`onecold device change descriptor : ${deviceChanged.deviceDescriptors[0].deviceType}`);
-
-      // 蓝牙设备类型为8
-      const isBluetooth = deviceChanged.deviceDescriptors[0].deviceType === 8;
-
-      if (isBluetooth) {
-        if (deviceChanged.type === 0) {
-          // 蓝牙连接
-          this.isBluetoothConnected = true;
-          console.info('onecold 蓝牙设备已连接');
-        } else if (deviceChanged.type === 1) {
-          // 蓝牙断开
-          this.isBluetoothConnected = false;
-          this.lastBluetoothLyricLine = '';
-          console.info('onecold 蓝牙设备已断开');
-
-          // 恢复原始元数据
-          if (this.currentSong) {
-            this.avSessionController.restoreOriginalMetadata(
-              this.currentSong.name || '',
-              this.currentSong.artist || ''
-            );
-          }
-
-          // 蓝牙断开暂停
-          if (this.bluetoothDisconnectPause && deviceChanged.deviceDescriptors[0].deviceRole === 2) {
-            console.info('heanup onecold 蓝牙音响断开, 暂停播放');
-            this.pause();
-          }
-        }
-      }
-    });
-
-    // 有线耳机断开和连接的监听。监听音频设备状态变化。
-    audioRoutingManager.on('deviceChange', audio.DeviceFlag.INPUT_DEVICES_FLAG, (deviceChanged: audio.DeviceChangeAction) => {
-      console.info('onecold device change type : ' + deviceChanged.type);  // 设备连接状态变化,0为连接,1为断开连接。
-      // 设备类型 22是USB耳机,带麦克风,3是有线耳机。
-      if(deviceChanged.type==1&&
-        (deviceChanged.deviceDescriptors[0].deviceType== 22||
-          deviceChanged.deviceDescriptors[0].deviceType==3)){
-        console.info('heanup onecold 有线耳机断开, 暂停播放')
-        this.pause();
-      }
-    });
+    this.audioRoutingManager = audioManager.getRoutingManager();  // 再调用AudioManager的方法创建AudioRoutingManager实例。
+    this.outputDeviceChangeCallback = (deviceChanged: audio.DeviceChangeAction) => {
+      this.handleOutputDeviceChange(deviceChanged)
+    }
+    this.audioRoutingManager.on('deviceChange', audio.DeviceFlag.OUTPUT_DEVICES_FLAG, this.outputDeviceChangeCallback);
 
 
     this.makeWorker()
@@ -1413,6 +1348,135 @@ export struct LocalMusic {
     const context = getContext(this) as common.UIAbilityContext
     context.getApplicationContext().setColorMode(colorMode)
   }
+
+  private isAudioPlaybackActive(): boolean {
+    return this.CONTROL_PlayStatus === PlayStatus.PLAY
+      || this.mIjkMediaPlayer.isPlaying()
+      || (!!this.castControllerWrapper && this.isCastPlaying)
+  }
+
+  private isBluetoothOutputDeviceType(deviceType: audio.DeviceType): boolean {
+    return deviceType === audio.DeviceType.BLUETOOTH_A2DP || deviceType === audio.DeviceType.BLUETOOTH_SCO
+  }
+
+  private isWiredOutputDeviceType(deviceType: audio.DeviceType): boolean {
+    return deviceType === audio.DeviceType.WIRED_HEADSET
+      || deviceType === audio.DeviceType.WIRED_HEADPHONES
+      || deviceType === audio.DeviceType.USB_HEADSET
+  }
+
+  private handleAudioInterrupt(event: InterruptEvent): void {
+    console.info(`heanup onecold 与其他应用音频冲突 被截断点event: ${JSON.stringify(event)}`)
+    if (this.isSameTimePlay) {
+      Logger.info(TAG, `音频中断已忽略,当前为混音模式: ${JSON.stringify(event)}`)
+      return
+    }
+    const wasPlayingBeforeInterrupt = this.isAudioPlaybackActive()
+    switch (event.hintType) {
+      case InterruptHintType.INTERRUPT_HINT_PAUSE:
+        this.audioInterruptResumePending = wasPlayingBeforeInterrupt
+        if (wasPlayingBeforeInterrupt) {
+          this.pause(false)
+        }
+        break
+      case InterruptHintType.INTERRUPT_HINT_RESUME:
+        if (this.audioInterruptResumePending && this.CONTROL_PlayStatus === PlayStatus.PAUSE) {
+          console.info(`heanup onecold audioInterrupt startPlayOrResumePlay: ${JSON.stringify(event)}`)
+          this.audioInterruptResumePending = false
+          this.resumePlaybackAfterAudioInterrupt()
+        }
+        break
+      case InterruptHintType.INTERRUPT_HINT_STOP:
+        Logger.info(TAG, `收到音频中断 STOP,改为暂停等待用户手动恢复: ${JSON.stringify(event)}`)
+        this.audioInterruptResumePending = false
+        if (wasPlayingBeforeInterrupt) {
+          this.pause(false)
+        }
+        break
+      default:
+        Logger.info(TAG, `忽略未处理的音频中断类型: ${JSON.stringify(event)}`)
+        break
+    }
+  }
+
+  private resumePlaybackAfterAudioInterrupt(): void {
+    if (this.castControllerWrapper && this.isCastPlaying) {
+      this.castControllerWrapper.setPlaying().then(() => {
+        this.CONTROL_PlayStatus = PlayStatus.PLAY
+        this.setIsPlaying(true)
+        this.updateSessionPlayState(true)
+        this.playChange()
+      }).catch((error: BusinessError | Error) => {
+        Logger.error(TAG, `音频中断后恢复投播失败: ${error}`)
+      })
+      return
+    }
+    this.startPlayOrResumePlay()
+  }
+
+  private handlePlayerDeviceChange(event: InterruptEvent): void {
+    console.info(`onecold Heanup deviceChange event: ${JSON.stringify(event)}`);
+    if (event.reason === DeviceChangeReason.REASON_NEW_DEVICE_AVAILABLE) {
+      Logger.info(TAG, 'ijk deviceChange 检测到新设备接入,交由 AudioRoutingManager 处理')
+    } else if (event.reason === DeviceChangeReason.REASON_OLD_DEVICE_UNAVAILABLE) {
+      Logger.info(TAG, 'ijk deviceChange 检测到旧设备移除,等待 AudioRoutingManager 精确判断是否暂停')
+    }
+  }
+
+  private handleOutputDeviceChange(deviceChanged: audio.DeviceChangeAction): void {
+    const descriptors = deviceChanged?.deviceDescriptors ?? []
+    if (descriptors.length <= 0) {
+      Logger.warn(TAG, `音频输出设备变化缺少描述信息: ${JSON.stringify(deviceChanged)}`)
+      return
+    }
+    Logger.info(TAG, `音频输出设备变化: type=${deviceChanged.type}, count=${descriptors.length}`)
+    const outputDescriptors = descriptors.filter((descriptor) => descriptor.deviceRole === audio.DeviceRole.OUTPUT_DEVICE)
+    if (outputDescriptors.length <= 0) {
+      Logger.info(TAG, `忽略非输出设备变化: ${JSON.stringify(deviceChanged)}`)
+      return
+    }
+    const hasBluetoothOutput = outputDescriptors.some((descriptor) =>
+      this.isBluetoothOutputDeviceType(descriptor.deviceType))
+    const hasWiredOutput = outputDescriptors.some((descriptor) =>
+      this.isWiredOutputDeviceType(descriptor.deviceType))
+
+    if (deviceChanged.type === audio.DeviceChangeType.CONNECT) {
+      if (hasBluetoothOutput) {
+        this.isBluetoothConnected = true
+        console.info('onecold 蓝牙设备已连接')
+      }
+      return
+    }
+
+    if (deviceChanged.type !== audio.DeviceChangeType.DISCONNECT) {
+      return
+    }
+
+    if (hasBluetoothOutput) {
+      this.isBluetoothConnected = false
+      this.lastBluetoothLyricLine = ''
+      console.info('onecold 蓝牙设备已断开')
+      if (this.currentSong) {
+        this.avSessionController.restoreOriginalMetadata(
+          this.currentSong.name || '',
+          this.currentSong.artist || ''
+        )
+      }
+      if (this.bluetoothDisconnectPause && this.isAudioPlaybackActive()) {
+        this.audioInterruptResumePending = false
+        console.info('heanup onecold 蓝牙音响断开, 暂停播放')
+        this.pause()
+      }
+      return
+    }
+
+    if (hasWiredOutput && this.isAudioPlaybackActive()) {
+      this.audioInterruptResumePending = false
+      console.info('heanup onecold 有线/USB耳机断开, 暂停播放')
+      this.pause()
+    }
+  }
+
   initSetting() {
     this.default_layout =  PreferencesUtil.getNumberSync('default_layout', 0)
     this.isGeKongPlayNext  = PreferencesUtil.getBooleanSync('isGeKongPlayNext', false)
@@ -2180,12 +2244,22 @@ export struct LocalMusic {
     emitter.off(EventConstants.EVENT_OPEN_LOCAL_SPECIAL_LIST);
     emitter.off(EventConstants.EVENT_PLAY_PAUSE);
     this.mDestroyPage = true;
+    this.audioInterruptResumePending = false;
     this.mIjkMediaPlayer.setScreenOnWhilePlaying(false);
     if (this.CONTROL_PlayStatus != PlayStatus.INIT) {
       this.stop();
     }
     this.mIjkMediaPlayer.off('audioInterrupt');
     this.mIjkMediaPlayer.off('deviceChange');
+    if (this.audioRoutingManager && this.outputDeviceChangeCallback) {
+      try {
+        this.audioRoutingManager.off('deviceChange', this.outputDeviceChangeCallback);
+      } catch (error) {
+        Logger.warn(TAG, `注销音频输出设备监听失败: ${(error as BusinessError).message ?? error}`);
+      }
+    }
+    this.outputDeviceChangeCallback = undefined;
+    this.audioRoutingManager = undefined;
     if (workerInstance) {
       workerInstance.terminate();
     }
@@ -18886,7 +18960,10 @@ export struct LocalMusic {
     }
   }
 
-  private async pause() {
+  private async pause(clearInterruptResumeFlag: boolean = true) {
+    if (clearInterruptResumeFlag) {
+      this.audioInterruptResumePending = false;
+    }
     // 如果是投播模式,使用 CastController 的 pause 方法
     if (this.castControllerWrapper && this.isCastPlaying) {
       try {
@@ -18899,11 +18976,13 @@ export struct LocalMusic {
       } catch (error) {
         Logger.error('heanup pause', `投播暂停失败: ${error}`);
       }
-    } else if (this.mIjkMediaPlayer.isPlaying()) {
+    } else if (this.mIjkMediaPlayer.isPlaying() || this.CONTROL_PlayStatus === PlayStatus.PLAY) {
       // 本地播放器模式
       this.savePlaybackPosition();
-      this.mIjkMediaPlayer.pause();
-      this.setProgress();
+      if (this.mIjkMediaPlayer.isPlaying()) {
+        this.mIjkMediaPlayer.pause();
+        this.setProgress();
+      }
       this.mDestroyPage = true;
       this.CONTROL_PlayStatus = PlayStatus.PAUSE;
       this.updateSessionPlayState(false)
@@ -18916,6 +18995,7 @@ export struct LocalMusic {
   }
 
   private stop() {
+    this.audioInterruptResumePending = false;
     this.clearInitialSeekGuardTimer();
     this.waitInitialSeekBeforeStart = false;
     this.pendingMemorySeekPosition = 0;