|
@@ -252,6 +252,15 @@ interface WorkerMessageEditResult {
|
|
|
data: WorkerEditMusicResult;
|
|
data: WorkerEditMusicResult;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+interface AlphaTaskItem {
|
|
|
|
|
+ type: number;
|
|
|
|
|
+ name?: string;
|
|
|
|
|
+ pyStr?: string;
|
|
|
|
|
+ fileName?: string;
|
|
|
|
|
+ artist?: string;
|
|
|
|
|
+ album?: string;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
type LocalMusicWorkerMessage =
|
|
type LocalMusicWorkerMessage =
|
|
|
WorkerMessageScanDone
|
|
WorkerMessageScanDone
|
|
|
| WorkerMessageMediaList
|
|
| WorkerMessageMediaList
|
|
@@ -373,6 +382,7 @@ export struct LocalMusic {
|
|
|
private alphaBetRebuildTimer: number = 0
|
|
private alphaBetRebuildTimer: number = 0
|
|
|
private alphaBetDirty: boolean = false
|
|
private alphaBetDirty: boolean = false
|
|
|
private alphaBetCacheKey: string = ''
|
|
private alphaBetCacheKey: string = ''
|
|
|
|
|
+ private alphaBetBuildVersion: number = 0
|
|
|
@State isShowPrecious: boolean = false //播控条显示上一首按钮
|
|
@State isShowPrecious: boolean = false //播控条显示上一首按钮
|
|
|
//背景两侧流光控制器
|
|
//背景两侧流光控制器
|
|
|
@State sceneBgController: HdsSceneController|undefined = deviceInfo.sdkApiVersion>=20
|
|
@State sceneBgController: HdsSceneController|undefined = deviceInfo.sdkApiVersion>=20
|
|
@@ -531,6 +541,11 @@ export struct LocalMusic {
|
|
|
private waterScrollLastPagingEnd: number = -1
|
|
private waterScrollLastPagingEnd: number = -1
|
|
|
private gridWaterScrollIdleTimer: number = 0
|
|
private gridWaterScrollIdleTimer: number = 0
|
|
|
private gridWaterLastActiveTs: number = 0
|
|
private gridWaterLastActiveTs: number = 0
|
|
|
|
|
+ private listUpdateTimer: number = 0
|
|
|
|
|
+ private listUpdateVersion: number = 0
|
|
|
|
|
+ private detailBackTransitioning: boolean = false
|
|
|
|
|
+ private mediaLibraryEnterTs: number = 0
|
|
|
|
|
+ private mediaLibraryWarmupPrefetchTimer: number = 0
|
|
|
private listVisibleStart: number = -1
|
|
private listVisibleStart: number = -1
|
|
|
private listVisibleEnd: number = -1
|
|
private listVisibleEnd: number = -1
|
|
|
private listThumbWarmupCheckAt: number = 0
|
|
private listThumbWarmupCheckAt: number = 0
|
|
@@ -639,10 +654,14 @@ export struct LocalMusic {
|
|
|
this.isRefreshing = false
|
|
this.isRefreshing = false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- async onModeChange(): Promise<void> {
|
|
|
|
|
|
|
+ async onModeChange(preserveList: boolean = false): Promise<void> {
|
|
|
this.isFavMusic = false
|
|
this.isFavMusic = false
|
|
|
this.isShowTitleBar = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_TITLTBAR, true)
|
|
this.isShowTitleBar = PreferencesUtil.getBooleanSync(SettingPage.IS_SHOW_TITLTBAR, true)
|
|
|
this.refreshDirVisibilitySettings()
|
|
this.refreshDirVisibilitySettings()
|
|
|
|
|
+ if (this.mediaLibraryWarmupPrefetchTimer) {
|
|
|
|
|
+ clearTimeout(this.mediaLibraryWarmupPrefetchTimer)
|
|
|
|
|
+ this.mediaLibraryWarmupPrefetchTimer = 0
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
Logger.info('heanup LocalMusic', `onModeChange: 切换模式 modeType=${this.modeType}`);
|
|
Logger.info('heanup LocalMusic', `onModeChange: 切换模式 modeType=${this.modeType}`);
|
|
|
|
|
|
|
@@ -653,18 +672,21 @@ export struct LocalMusic {
|
|
|
// 切换模式时加载对应的排序类型
|
|
// 切换模式时加载对应的排序类型
|
|
|
this.titleName = Utility.resourceToString(this.context,$r('app.string.home'))
|
|
this.titleName = Utility.resourceToString(this.context,$r('app.string.home'))
|
|
|
this.sortType = this.loadSortTypeForCurrentMode()
|
|
this.sortType = this.loadSortTypeForCurrentMode()
|
|
|
|
|
+ this.mediaLibraryEnterTs = 0
|
|
|
break
|
|
break
|
|
|
case 1:
|
|
case 1:
|
|
|
this.rightTopImage = $r('sys.symbol.sort')
|
|
this.rightTopImage = $r('sys.symbol.sort')
|
|
|
this.titleName = Utility.resourceToString(this.context,$r('app.string.media_ku'))
|
|
this.titleName = Utility.resourceToString(this.context,$r('app.string.media_ku'))
|
|
|
// 切换模式时加载对应的排序类型
|
|
// 切换模式时加载对应的排序类型
|
|
|
this.sortType = this.loadSortTypeForCurrentMode()
|
|
this.sortType = this.loadSortTypeForCurrentMode()
|
|
|
|
|
+ this.mediaLibraryEnterTs = Date.now()
|
|
|
break
|
|
break
|
|
|
case 2:
|
|
case 2:
|
|
|
this.rightTopImage = $r('sys.symbol.sort')
|
|
this.rightTopImage = $r('sys.symbol.sort')
|
|
|
this.titleName = Utility.resourceToString(this.context,$r('app.string.artist'))
|
|
this.titleName = Utility.resourceToString(this.context,$r('app.string.artist'))
|
|
|
// 切换到艺术家模式时,加载艺术家模式的排序类型(默认按数量降序)
|
|
// 切换到艺术家模式时,加载艺术家模式的排序类型(默认按数量降序)
|
|
|
this.sortType = this.loadSortTypeForCurrentMode()
|
|
this.sortType = this.loadSortTypeForCurrentMode()
|
|
|
|
|
+ this.mediaLibraryEnterTs = 0
|
|
|
break
|
|
break
|
|
|
|
|
|
|
|
case 3:
|
|
case 3:
|
|
@@ -672,10 +694,11 @@ export struct LocalMusic {
|
|
|
this.titleName = Utility.resourceToString(this.context,$r('app.string.album'))
|
|
this.titleName = Utility.resourceToString(this.context,$r('app.string.album'))
|
|
|
// 切换到专辑模式时,加载专辑模式的排序类型(默认按数量降序)
|
|
// 切换到专辑模式时,加载专辑模式的排序类型(默认按数量降序)
|
|
|
this.sortType = this.loadSortTypeForCurrentMode()
|
|
this.sortType = this.loadSortTypeForCurrentMode()
|
|
|
|
|
+ this.mediaLibraryEnterTs = 0
|
|
|
break
|
|
break
|
|
|
}
|
|
}
|
|
|
// 模式切换时重置分页状态并加载第一页
|
|
// 模式切换时重置分页状态并加载第一页
|
|
|
- await this.resetAndLoadFirstPage();
|
|
|
|
|
|
|
+ await this.resetAndLoadFirstPage(!preserveList);
|
|
|
|
|
|
|
|
Logger.info('heanup onModeChange', `doSortType: 切换排序方式 sortType=${this.sortType}, mode=${this.modeType}`);
|
|
Logger.info('heanup onModeChange', `doSortType: 切换排序方式 sortType=${this.sortType}, mode=${this.modeType}`);
|
|
|
}
|
|
}
|
|
@@ -793,12 +816,27 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
this.isZero = false
|
|
this.isZero = false
|
|
|
if (this.modeType !== 0 && this.isCanBack) {
|
|
if (this.modeType !== 0 && this.isCanBack) {
|
|
|
|
|
+ if (this.detailBackTransitioning) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ this.detailBackTransitioning = true
|
|
|
// 先设置 isCanBack=false,确保 onModeChange 读取的是列表页的排序设置
|
|
// 先设置 isCanBack=false,确保 onModeChange 读取的是列表页的排序设置
|
|
|
this.isCanBack = false
|
|
this.isCanBack = false
|
|
|
- this.onModeChange()
|
|
|
|
|
|
|
+ this.rightTopImage = $r('sys.symbol.sort')
|
|
|
|
|
+ if (this.modeType == 2) {
|
|
|
|
|
+ this.titleName = Utility.resourceToString(this.context,$r('app.string.artist'))
|
|
|
|
|
+ if (ArrayUtil.isNotEmpty(this.artistList)) {
|
|
|
|
|
+ this.updateListData(this.artistList, true, true)
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (this.modeType == 3) {
|
|
|
|
|
+ this.titleName = Utility.resourceToString(this.context,$r('app.string.album'))
|
|
|
|
|
+ if (ArrayUtil.isNotEmpty(this.albumList)) {
|
|
|
|
|
+ this.updateListData(this.albumList, true, true)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 返回专辑列表时恢复滚动偏移量,支持列表和网格
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
|
|
+ // 返回列表页时保留当前列表,避免先清空导致闪现
|
|
|
|
|
+ void this.onModeChange(true).then(() => {
|
|
|
if (this.modeType == 2) {
|
|
if (this.modeType == 2) {
|
|
|
if (this.isGridMusic||this.twoFingerType==4) {
|
|
if (this.isGridMusic||this.twoFingerType==4) {
|
|
|
let lastOffset = this.mScrollMap.get('lastGridArtistScrollOffset') ?? 0
|
|
let lastOffset = this.mScrollMap.get('lastGridArtistScrollOffset') ?? 0
|
|
@@ -826,10 +864,14 @@ export struct LocalMusic {
|
|
|
this.listScroller.scrollTo({ xOffset: 0, yOffset: lastOffset })
|
|
this.listScroller.scrollTo({ xOffset: 0, yOffset: lastOffset })
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- }, 200)
|
|
|
|
|
|
|
+ this.detailBackTransitioning = false
|
|
|
|
|
+ }).catch((error: Error) => {
|
|
|
|
|
+ Logger.error('heanup LocalMusic', `doSwipBack onModeChange error: ${error.message}`)
|
|
|
|
|
+ this.detailBackTransitioning = false
|
|
|
|
|
+ })
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
+ this.mediaLibraryEnterTs = 0
|
|
|
|
|
|
|
|
if (this.isHistory || this.isFavMusic) {
|
|
if (this.isHistory || this.isFavMusic) {
|
|
|
this.isHistory = false
|
|
this.isHistory = false
|
|
@@ -866,8 +908,14 @@ export struct LocalMusic {
|
|
|
|
|
|
|
|
this.currentPath = Utility.getParentDirectory(this.currentPath)
|
|
this.currentPath = Utility.getParentDirectory(this.currentPath)
|
|
|
Logger.info('this.currentPath2 = ' + this.currentPath)
|
|
Logger.info('this.currentPath2 = ' + this.currentPath)
|
|
|
- this.getSortedFiles(this.currentPath)
|
|
|
|
|
|
|
+ this.getSortedFiles(this.currentPath, false, undefined, undefined, undefined, true)
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ private triggerSwipeBackWithAnimation(): void {
|
|
|
|
|
+ animateTo({ duration: 555 }, () => {
|
|
|
|
|
+ this.doSwipBack()
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
// 组件生命周期
|
|
// 组件生命周期
|
|
|
aboutToAppear() {
|
|
aboutToAppear() {
|
|
@@ -913,11 +961,7 @@ export struct LocalMusic {
|
|
|
let eventUpdateSwipeBack: emitter.InnerEvent = { eventId: EventConstants.EVENT_SWIPE_BACK_UPDATE }
|
|
let eventUpdateSwipeBack: emitter.InnerEvent = { eventId: EventConstants.EVENT_SWIPE_BACK_UPDATE }
|
|
|
// 监听广播事件(手势返回处理)
|
|
// 监听广播事件(手势返回处理)
|
|
|
emitter.on(eventUpdateSwipeBack, (eventData: emitter.EventData) => {
|
|
emitter.on(eventUpdateSwipeBack, (eventData: emitter.EventData) => {
|
|
|
-
|
|
|
|
|
- animateTo({ duration: 555 }, () => {
|
|
|
|
|
- this.doSwipBack()
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
|
|
+ this.triggerSwipeBackWithAnimation()
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
//ScanFilePage广播接收时间
|
|
//ScanFilePage广播接收时间
|
|
@@ -1286,7 +1330,7 @@ export struct LocalMusic {
|
|
|
this.bluetoothDisconnectPause = PreferencesUtil.getBooleanSync(SettingPage.BLUETOOTH_DISCONNECT_PAUSE, false)
|
|
this.bluetoothDisconnectPause = PreferencesUtil.getBooleanSync(SettingPage.BLUETOOTH_DISCONNECT_PAUSE, false)
|
|
|
this.bluetoothLyricEnabled = PreferencesUtil.getBooleanSync(SettingPage.BLUETOOTH_LYRIC_ENABLED, false)
|
|
this.bluetoothLyricEnabled = PreferencesUtil.getBooleanSync(SettingPage.BLUETOOTH_LYRIC_ENABLED, false)
|
|
|
this.bluetoothLyricMode = PreferencesUtil.getNumberSync(SettingPage.BLUETOOTH_LYRIC_MODE, 0)
|
|
this.bluetoothLyricMode = PreferencesUtil.getNumberSync(SettingPage.BLUETOOTH_LYRIC_MODE, 0)
|
|
|
- this.isShowSpectrum = PreferencesUtil.getBooleanSync('isShowSpectrum', false)
|
|
|
|
|
|
|
+ this.isShowSpectrum = PreferencesUtil.getBooleanSync('isShowSpectrum', true)
|
|
|
this.spectrumModeIndex = PreferencesUtil.getNumberSync('spectrumModeIndex', 0)
|
|
this.spectrumModeIndex = PreferencesUtil.getNumberSync('spectrumModeIndex', 0)
|
|
|
this.enablePointLight = PreferencesUtil.getBooleanSync('enablePointLight', true)
|
|
this.enablePointLight = PreferencesUtil.getBooleanSync('enablePointLight', true)
|
|
|
AppStorage.setOrCreate('EnablePointLight', this.enablePointLight);
|
|
AppStorage.setOrCreate('EnablePointLight', this.enablePointLight);
|
|
@@ -2005,6 +2049,15 @@ export struct LocalMusic {
|
|
|
clearTimeout(this.gridWaterScrollIdleTimer);
|
|
clearTimeout(this.gridWaterScrollIdleTimer);
|
|
|
this.gridWaterScrollIdleTimer = 0;
|
|
this.gridWaterScrollIdleTimer = 0;
|
|
|
}
|
|
}
|
|
|
|
|
+ if (this.listUpdateTimer) {
|
|
|
|
|
+ clearTimeout(this.listUpdateTimer);
|
|
|
|
|
+ this.listUpdateTimer = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.mediaLibraryWarmupPrefetchTimer) {
|
|
|
|
|
+ clearTimeout(this.mediaLibraryWarmupPrefetchTimer);
|
|
|
|
|
+ this.mediaLibraryWarmupPrefetchTimer = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.alphaBetBuildVersion++;
|
|
|
this.gridWaterLastActiveTs = 0;
|
|
this.gridWaterLastActiveTs = 0;
|
|
|
this.coverThumbCache.dispose();
|
|
this.coverThumbCache.dispose();
|
|
|
this.coverThumbCacheReady = false;
|
|
this.coverThumbCacheReady = false;
|
|
@@ -2038,7 +2091,8 @@ export struct LocalMusic {
|
|
|
this.getUIContext().getHostContext()!.eventHub.off('openPlayList');
|
|
this.getUIContext().getHostContext()!.eventHub.off('openPlayList');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- async getSortedFiles(curPath: string, isWorkerPost?: boolean, destPath?: string, isOpen?: boolean,openItem?:VideoItem) {
|
|
|
|
|
|
|
+ async getSortedFiles(curPath: string, isWorkerPost?: boolean, destPath?: string, isOpen?: boolean,
|
|
|
|
|
+ openItem?: VideoItem, immediateListUpdate?: boolean, scrollTopAfterUpdate?: boolean) {
|
|
|
this.isFavMusic = false
|
|
this.isFavMusic = false
|
|
|
console.info('onecold getSortedFiles 1 curPath '+curPath)
|
|
console.info('onecold getSortedFiles 1 curPath '+curPath)
|
|
|
if (isWorkerPost) {
|
|
if (isWorkerPost) {
|
|
@@ -2083,7 +2137,10 @@ export struct LocalMusic {
|
|
|
this.currentTitleCover = Utility.getFirstCoverFromList(cachedValue)
|
|
this.currentTitleCover = Utility.getFirstCoverFromList(cachedValue)
|
|
|
}
|
|
}
|
|
|
this.isRefreshing = false
|
|
this.isRefreshing = false
|
|
|
- this.updateListData(cachedValue)
|
|
|
|
|
|
|
+ this.updateListData(cachedValue, undefined, immediateListUpdate)
|
|
|
|
|
+ if (scrollTopAfterUpdate) {
|
|
|
|
|
+ this.scrollToTopForCurrentLayout()
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (cachedDirList !== undefined) {
|
|
if (cachedDirList !== undefined) {
|
|
@@ -2150,7 +2207,10 @@ export struct LocalMusic {
|
|
|
// 合并文件夹和文件,文件夹在前,文件在后,广告在中间
|
|
// 合并文件夹和文件,文件夹在前,文件在后,广告在中间
|
|
|
this.videoLocalList = directories.concat(files);
|
|
this.videoLocalList = directories.concat(files);
|
|
|
|
|
|
|
|
- this.updateListData(this.videoLocalList)
|
|
|
|
|
|
|
+ this.updateListData(this.videoLocalList, undefined, immediateListUpdate)
|
|
|
|
|
+ if (scrollTopAfterUpdate) {
|
|
|
|
|
+ this.scrollToTopForCurrentLayout()
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
if (ArrayUtil.isNotEmpty(files)) {
|
|
if (ArrayUtil.isNotEmpty(files)) {
|
|
|
if (this.currentPath != this.rootPath) {
|
|
if (this.currentPath != this.rootPath) {
|
|
@@ -2238,12 +2298,23 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
return mediaItems;
|
|
return mediaItems;
|
|
|
}
|
|
}
|
|
|
- updateListData(mList: Array<VideoItem>, noSort?: boolean) {
|
|
|
|
|
|
|
+ updateListData(mList: Array<VideoItem>, noSort?: boolean, immediate?: boolean) {
|
|
|
this.isShowTitleBar = true
|
|
this.isShowTitleBar = true
|
|
|
- this.getUIContext().animateTo({ duration: 666 }, () => {
|
|
|
|
|
- this.opacityItem = 0.5;
|
|
|
|
|
- });
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
|
|
+ const shouldAnimate = !immediate
|
|
|
|
|
+ if (this.listUpdateTimer) {
|
|
|
|
|
+ clearTimeout(this.listUpdateTimer)
|
|
|
|
|
+ this.listUpdateTimer = 0
|
|
|
|
|
+ }
|
|
|
|
|
+ const updateVersion = ++this.listUpdateVersion
|
|
|
|
|
+ if (shouldAnimate) {
|
|
|
|
|
+ this.getUIContext().animateTo({ duration: 666 }, () => {
|
|
|
|
|
+ this.opacityItem = 0.5;
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ const applyListData = () => {
|
|
|
|
|
+ if (updateVersion !== this.listUpdateVersion) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
this.videoLocalList = mList;
|
|
this.videoLocalList = mList;
|
|
|
if (!noSort) {
|
|
if (!noSort) {
|
|
|
if (this.modeType === 0 || this.modeType === 1) {
|
|
if (this.modeType === 0 || this.modeType === 1) {
|
|
@@ -2252,12 +2323,24 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
this.dataSource.pushArrayData(this.videoLocalList)
|
|
this.dataSource.pushArrayData(this.videoLocalList)
|
|
|
- this.getUIContext().animateTo({ duration: 666 }, () => {
|
|
|
|
|
|
|
+ if (shouldAnimate) {
|
|
|
|
|
+ this.getUIContext().animateTo({ duration: 666 }, () => {
|
|
|
|
|
+ this.opacityItem = 1;
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
this.opacityItem = 1;
|
|
this.opacityItem = 1;
|
|
|
- });
|
|
|
|
|
|
|
+ }
|
|
|
this.setButtonStatus()
|
|
this.setButtonStatus()
|
|
|
//添加到字母索引表
|
|
//添加到字母索引表
|
|
|
this.markAlphaBetDirty()
|
|
this.markAlphaBetDirty()
|
|
|
|
|
+ };
|
|
|
|
|
+ if (immediate) {
|
|
|
|
|
+ applyListData();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.listUpdateTimer = setTimeout(() => {
|
|
|
|
|
+ this.listUpdateTimer = 0
|
|
|
|
|
+ applyListData();
|
|
|
}, 200);
|
|
}, 200);
|
|
|
|
|
|
|
|
}
|
|
}
|
|
@@ -2966,6 +3049,88 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private async pickCoverImageToSandbox(): Promise<string | undefined> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const photoPicker = new photoAccessHelper.PhotoViewPicker();
|
|
|
|
|
+ const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
|
|
|
|
|
+ photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
|
|
|
|
|
+ photoSelectOptions.maxSelectNumber = 1;
|
|
|
|
|
+ const photoSelectResult = await photoPicker.select(photoSelectOptions);
|
|
|
|
|
+ const selectUris = photoSelectResult.photoUris;
|
|
|
|
|
+ if (!selectUris || selectUris.length === 0) {
|
|
|
|
|
+ return undefined;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const coverDir = this.context.filesDir + FileUtil.separator + 'ttmusic_cover';
|
|
|
|
|
+ if (!FileUtil.accessSync(coverDir)) {
|
|
|
|
|
+ FileUtil.mkdirSync(coverDir);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const sourceUri = selectUris[0];
|
|
|
|
|
+ const sourceName = FileUtil.getFileName(sourceUri) || '';
|
|
|
|
|
+ let suffix = '.jpg';
|
|
|
|
|
+ const dotIndex = sourceName.lastIndexOf('.');
|
|
|
|
|
+ if (dotIndex >= 0) {
|
|
|
|
|
+ suffix = sourceName.substring(dotIndex);
|
|
|
|
|
+ }
|
|
|
|
|
+ const targetName = `${Date.now()}_${await MD5.digestSync(sourceUri)}${suffix}`;
|
|
|
|
|
+ const targetPath = coverDir + FileUtil.separator + targetName;
|
|
|
|
|
+
|
|
|
|
|
+ const sourceFile = fileIo.openSync(sourceUri, fileIo.OpenMode.READ_ONLY);
|
|
|
|
|
+ const targetFile = fileIo.openSync(targetPath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
|
|
|
|
|
+ fileIo.copyFileSync(sourceFile.fd, targetFile.fd);
|
|
|
|
|
+ fileIo.closeSync(sourceFile.fd);
|
|
|
|
|
+ fileIo.closeSync(targetFile.fd);
|
|
|
|
|
+
|
|
|
|
|
+ return fileUri.getUriFromPath(targetPath);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ const err = error as BusinessError;
|
|
|
|
|
+ Logger.error(TAG, `pickCoverImageToSandbox failed, code: ${err.code}, message: ${err.message}`);
|
|
|
|
|
+ return undefined;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private async replaceCurrentSongCoverByAlbum(): Promise<void> {
|
|
|
|
|
+ if (!this.currentSong || StrUtil.isEmpty(this.currentSong.filePath)) {
|
|
|
|
|
+ ToastUtil.showToast('当前歌曲无效');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.currentSong.type !== CommonConstants.TYPE_LOCAL) {
|
|
|
|
|
+ ToastUtil.showToast('仅支持本地歌曲更换封面');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const sandboxCoverUri = await this.pickCoverImageToSandbox();
|
|
|
|
|
+ if (StrUtil.isEmpty(sandboxCoverUri)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.loadingDialogId = DialogHelper.showLoadingDialog();
|
|
|
|
|
+ try {
|
|
|
|
|
+ const task = new taskpool.Task(
|
|
|
|
|
+ replaceLocalMusicCoverTask,
|
|
|
|
|
+ this.context,
|
|
|
|
|
+ this.currentSong.filePath,
|
|
|
|
|
+ FileUtil.getFilePath(sandboxCoverUri!)
|
|
|
|
|
+ );
|
|
|
|
|
+ const result = await taskpool.execute(task, taskpool.Priority.HIGH) as boolean;
|
|
|
|
|
+ if (!result) {
|
|
|
|
|
+ ToastUtil.showToast('更换封面失败');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.cover = sandboxCoverUri;
|
|
|
|
|
+ this.currentSong.pixelMapPath = sandboxCoverUri;
|
|
|
|
|
+ await this.doUpateEditedFields(this.currentSong);
|
|
|
|
|
+ ToastUtil.showToast('更换封面成功');
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ Logger.error(TAG, `replaceCurrentSongCoverByAlbum failed: ${JSON.stringify(error)}`);
|
|
|
|
|
+ ToastUtil.showToast('更换封面失败');
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ DialogHelper.closeDialog(this.loadingDialogId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 拉起picker选择文件管理器
|
|
// 拉起picker选择文件管理器
|
|
|
async callFilePickerSelectFile(): Promise<void> {
|
|
async callFilePickerSelectFile(): Promise<void> {
|
|
|
try {
|
|
try {
|
|
@@ -3593,7 +3758,7 @@ export struct LocalMusic {
|
|
|
Column({space: 5}){
|
|
Column({space: 5}){
|
|
|
if(this.showZMIndex&&this.enableShowAlphabet){
|
|
if(this.showZMIndex&&this.enableShowAlphabet){
|
|
|
if(this.isShowAlphaBet&&!(this.modeType==2&&!this.isCanBack)
|
|
if(this.isShowAlphaBet&&!(this.modeType==2&&!this.isCanBack)
|
|
|
- &&!(this.modeType==3&&!this.isCanBack)){
|
|
|
|
|
|
|
+ &&!(this.modeType==3&&!this.isCanBack)&&(this.sortType==4||this.sortType==5)){
|
|
|
AlphabetIndexer({arrayValue: this.alphaBet,selected: this.selectAlphaBetIndex})
|
|
AlphabetIndexer({arrayValue: this.alphaBet,selected: this.selectAlphaBetIndex})
|
|
|
.attributeModifier(this.alphaBetStyle)
|
|
.attributeModifier(this.alphaBetStyle)
|
|
|
.usingPopup(this.isTouchingAlphaBet)
|
|
.usingPopup(this.isTouchingAlphaBet)
|
|
@@ -3746,6 +3911,11 @@ export struct LocalMusic {
|
|
|
if (!this.showZMIndex) {
|
|
if (!this.showZMIndex) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+ if (this.isMediaLibraryWarmupWindow()) {
|
|
|
|
|
+ this.alphaBetDirty = true;
|
|
|
|
|
+ this.scheduleAlphaBetRebuild(900);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
this.alphaBetDirty = true;
|
|
this.alphaBetDirty = true;
|
|
|
if (!this.isScrolling) {
|
|
if (!this.isScrolling) {
|
|
|
this.scheduleAlphaBetRebuild(80);
|
|
this.scheduleAlphaBetRebuild(80);
|
|
@@ -3837,6 +4007,34 @@ export struct LocalMusic {
|
|
|
return 0
|
|
return 0
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private buildAlphaTaskItems(songs: Array<VideoItem>): Array<AlphaTaskItem> {
|
|
|
|
|
+ const items: Array<AlphaTaskItem> = []
|
|
|
|
|
+ for (const song of songs) {
|
|
|
|
|
+ items.push({
|
|
|
|
|
+ type: song.type,
|
|
|
|
|
+ name: song.name,
|
|
|
|
|
+ pyStr: song.pyStr,
|
|
|
|
|
+ fileName: song.fileName,
|
|
|
|
|
+ artist: song.artist,
|
|
|
|
|
+ album: song.album
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ return items
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private async buildAlphaBetByTask(songs: Array<VideoItem>, version: number): Promise<void> {
|
|
|
|
|
+ const taskItems = this.buildAlphaTaskItems(songs)
|
|
|
|
|
+ const task = new taskpool.Task(buildAlphabetIndexTask, taskItems, this.sortType, this.isShowFileName)
|
|
|
|
|
+ const result = await taskpool.execute(task, taskpool.Priority.MEDIUM) as Array<string>
|
|
|
|
|
+ if (version !== this.alphaBetBuildVersion) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ this.alphaBet = result
|
|
|
|
|
+ if (this.videoLocalList.length > 0) {
|
|
|
|
|
+ this.selectAlphaBetIndex = 0
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
async setAlphaBet(){
|
|
async setAlphaBet(){
|
|
|
if(!this.showZMIndex){
|
|
if(!this.showZMIndex){
|
|
|
return
|
|
return
|
|
@@ -3845,53 +4043,17 @@ export struct LocalMusic {
|
|
|
await this.refreshAlphaBetFromMediaLibrary()
|
|
await this.refreshAlphaBetFromMediaLibrary()
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- return new Promise<void>((resolve,reject) => {
|
|
|
|
|
- try {
|
|
|
|
|
- let songs = this.videoLocalList
|
|
|
|
|
- if(songs.length > 40000){
|
|
|
|
|
- this.alphaBet = []
|
|
|
|
|
- resolve()
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- let alphabetSet = new Set<string>();
|
|
|
|
|
- for (let song of songs) {
|
|
|
|
|
- let text = this.isShowFileName?song.fileName:
|
|
|
|
|
- (song.type!=CommonConstants.TYPE_IS_DIR&&song.pyStr)?song.pyStr:song.name
|
|
|
|
|
- if(this.sortType === 0||this.sortType==1){
|
|
|
|
|
- text = song.artist
|
|
|
|
|
- }else if(this.sortType === 2||this.sortType==3){
|
|
|
|
|
- text = song.album
|
|
|
|
|
- }
|
|
|
|
|
- if(text){
|
|
|
|
|
- let firstPinyin = getFirstLetter(text)
|
|
|
|
|
- if(!alphabetSet.has(firstPinyin)){
|
|
|
|
|
- alphabetSet.add(firstPinyin)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- // const pyStr = song.pyStr
|
|
|
|
|
- // if(pyStr){
|
|
|
|
|
- // if(!alphabetSet.has(pyStr)){
|
|
|
|
|
- // alphabetSet.add(pyStr)
|
|
|
|
|
- // }
|
|
|
|
|
- // }
|
|
|
|
|
-
|
|
|
|
|
- }
|
|
|
|
|
- this.alphaBet = Array.from(alphabetSet).sort((a,b)=>{
|
|
|
|
|
- if(this.sortType ===4||this.sortType==0||this.sortType==2||this.sortType==6){
|
|
|
|
|
- return a.localeCompare(b)
|
|
|
|
|
- }else{
|
|
|
|
|
- return b.localeCompare(a)
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- if(this.videoLocalList.length > 0){
|
|
|
|
|
- this.selectAlphaBetIndex = 0
|
|
|
|
|
- }
|
|
|
|
|
- resolve()
|
|
|
|
|
- }catch (e) {
|
|
|
|
|
- console.error('testTag','排序字母表出错',JSON.stringify(e))
|
|
|
|
|
- resolve()
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ const songs = this.videoLocalList
|
|
|
|
|
+ if (songs.length > 40000) {
|
|
|
|
|
+ this.alphaBet = []
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
- })
|
|
|
|
|
|
|
+ const version = ++this.alphaBetBuildVersion
|
|
|
|
|
+ await this.buildAlphaBetByTask(songs, version)
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('testTag','排序字母表出错',JSON.stringify(e))
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private async refreshAlphaBetFromMediaLibrary(): Promise<void> {
|
|
private async refreshAlphaBetFromMediaLibrary(): Promise<void> {
|
|
@@ -3914,29 +4076,8 @@ export struct LocalMusic {
|
|
|
searchKeyword: this.isSearchMode ? this.searchText : undefined,
|
|
searchKeyword: this.isSearchMode ? this.searchText : undefined,
|
|
|
isShowFileName: this.isShowFileName
|
|
isShowFileName: this.isShowFileName
|
|
|
})
|
|
})
|
|
|
- let alphabetSet = new Set<string>()
|
|
|
|
|
- for (let song of result.items) {
|
|
|
|
|
- let text = this.isShowFileName ? song.fileName :
|
|
|
|
|
- (song.type != CommonConstants.TYPE_IS_DIR && song.pyStr) ? song.pyStr : song.name
|
|
|
|
|
- if (this.sortType === 0 || this.sortType == 1) {
|
|
|
|
|
- text = song.artist
|
|
|
|
|
- } else if (this.sortType === 2 || this.sortType == 3) {
|
|
|
|
|
- text = song.album
|
|
|
|
|
- }
|
|
|
|
|
- if (text) {
|
|
|
|
|
- let firstPinyin = getFirstLetter(text)
|
|
|
|
|
- if (!alphabetSet.has(firstPinyin)) {
|
|
|
|
|
- alphabetSet.add(firstPinyin)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- this.alphaBet = Array.from(alphabetSet).sort((a, b) => {
|
|
|
|
|
- if (this.sortType === 4 || this.sortType == 0 || this.sortType == 2 || this.sortType == 6) {
|
|
|
|
|
- return a.localeCompare(b)
|
|
|
|
|
- } else {
|
|
|
|
|
- return b.localeCompare(a)
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ const version = ++this.alphaBetBuildVersion
|
|
|
|
|
+ await this.buildAlphaBetByTask(result.items, version)
|
|
|
this.alphaBetCacheKey = cacheKey
|
|
this.alphaBetCacheKey = cacheKey
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -3955,7 +4096,7 @@ export struct LocalMusic {
|
|
|
.clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
|
|
.clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.6 })
|
|
|
.animation({ duration: 300, curve: Curve.Ease })
|
|
.animation({ duration: 300, curve: Curve.Ease })
|
|
|
.onClick(() => {
|
|
.onClick(() => {
|
|
|
- this.doSwipBack()
|
|
|
|
|
|
|
+ this.triggerSwipeBackWithAnimation()
|
|
|
})
|
|
})
|
|
|
.attributeModifier(new ShadowModifier())
|
|
.attributeModifier(new ShadowModifier())
|
|
|
.zIndex(0)
|
|
.zIndex(0)
|
|
@@ -4087,11 +4228,15 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
.padding({ top: this.topSafeHeight+10, left: 10, right: 10 })
|
|
.padding({ top: this.topSafeHeight+10, left: 10, right: 10 })
|
|
|
.width('100%')
|
|
.width('100%')
|
|
|
- .visibility(this.isShowTitleBar?Visibility.Visible:
|
|
|
|
|
- this.autoHideTitle?Visibility.None:Visibility.Visible)
|
|
|
|
|
|
|
+ .visibility(Visibility.Visible)
|
|
|
|
|
+ .translate({
|
|
|
|
|
+ x: 0,
|
|
|
|
|
+ y: (this.isShowTitleBar || !this.autoHideTitle) ? 0 : -80
|
|
|
|
|
+ })
|
|
|
|
|
+ .opacity((this.isShowTitleBar || !this.autoHideTitle) ? 1 : 0)
|
|
|
.animation({
|
|
.animation({
|
|
|
- duration: 500,
|
|
|
|
|
- curve: Curve.Friction // 可选动画曲线
|
|
|
|
|
|
|
+ duration: 240,
|
|
|
|
|
+ curve: Curve.EaseInOut
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -6622,10 +6767,13 @@ export struct LocalMusic {
|
|
|
this.listVisibleStart = start
|
|
this.listVisibleStart = start
|
|
|
this.listVisibleEnd = end
|
|
this.listVisibleEnd = end
|
|
|
this.lastVisibleEndIndex = end
|
|
this.lastVisibleEndIndex = end
|
|
|
- this.warmupListVisibleThumbs(start, end)
|
|
|
|
|
|
|
+ if (!this.isMediaLibraryWarmupWindow()) {
|
|
|
|
|
+ this.warmupListVisibleThumbs(start, end)
|
|
|
|
|
+ }
|
|
|
const now = Date.now()
|
|
const now = Date.now()
|
|
|
const alphaDelta = this.listScrollLastAlphaStart >= 0 ? Math.abs(start - this.listScrollLastAlphaStart) : 999
|
|
const alphaDelta = this.listScrollLastAlphaStart >= 0 ? Math.abs(start - this.listScrollLastAlphaStart) : 999
|
|
|
- if (start >= 0 && start < this.videoLocalList.length &&
|
|
|
|
|
|
|
+ if (!this.isMediaLibraryWarmupWindow() &&
|
|
|
|
|
+ start >= 0 && start < this.videoLocalList.length &&
|
|
|
(alphaDelta >= 2 || now - this.listScrollAlphaCheckAt >= 48)) {
|
|
(alphaDelta >= 2 || now - this.listScrollAlphaCheckAt >= 48)) {
|
|
|
this.listScrollAlphaCheckAt = now
|
|
this.listScrollAlphaCheckAt = now
|
|
|
this.listScrollLastAlphaStart = start
|
|
this.listScrollLastAlphaStart = start
|
|
@@ -6803,6 +6951,7 @@ export struct LocalMusic {
|
|
|
switch (item.type) {
|
|
switch (item.type) {
|
|
|
case CommonConstants.TYPE_IS_DIR:
|
|
case CommonConstants.TYPE_IS_DIR:
|
|
|
// 进入歌单前安全保存当前滚动偏移量,支持列表和网格
|
|
// 进入歌单前安全保存当前滚动偏移量,支持列表和网格
|
|
|
|
|
+ let shouldScrollTopNow = true
|
|
|
let offsetB = 0
|
|
let offsetB = 0
|
|
|
if(this.twoFingerType==4){
|
|
if(this.twoFingerType==4){
|
|
|
if (this.waterScroller && typeof this.waterScroller.currentOffset === 'function') {
|
|
if (this.waterScroller && typeof this.waterScroller.currentOffset === 'function') {
|
|
@@ -6865,20 +7014,23 @@ export struct LocalMusic {
|
|
|
this.currentTitleName = item.name
|
|
this.currentTitleName = item.name
|
|
|
this.currentPath = item.filePath
|
|
this.currentPath = item.filePath
|
|
|
this.currentTitleCover = $r('app.media.alt')
|
|
this.currentTitleCover = $r('app.media.alt')
|
|
|
- this.getSortedFiles(this.currentPath)
|
|
|
|
|
|
|
+ shouldScrollTopNow = false
|
|
|
|
|
+ this.getSortedFiles(this.currentPath, false, undefined, undefined, undefined, true, true)
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
//进入歌单的时候的滚动位置在顶部
|
|
//进入歌单的时候的滚动位置在顶部
|
|
|
- if(this.twoFingerType==4){
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- this.waterScroller.scrollEdge(Edge.Top)
|
|
|
|
|
- this.scroller.scrollEdge(Edge.Top)
|
|
|
|
|
- }, 200)
|
|
|
|
|
- }else{
|
|
|
|
|
- if (this.isGridMusic) {
|
|
|
|
|
- this.scroller.scrollToIndex(0,false)
|
|
|
|
|
|
|
+ if (shouldScrollTopNow) {
|
|
|
|
|
+ if(this.twoFingerType==4){
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ this.waterScroller.scrollEdge(Edge.Top)
|
|
|
|
|
+ this.scroller.scrollEdge(Edge.Top)
|
|
|
|
|
+ }, 200)
|
|
|
}else{
|
|
}else{
|
|
|
- this.listScroller.scrollToIndex(0,false)
|
|
|
|
|
|
|
+ if (this.isGridMusic) {
|
|
|
|
|
+ this.scroller.scrollToIndex(0,false)
|
|
|
|
|
+ }else{
|
|
|
|
|
+ this.listScroller.scrollToIndex(0,false)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
this.rightTopImage = $r('sys.symbol.chevron_left')
|
|
this.rightTopImage = $r('sys.symbol.chevron_left')
|
|
@@ -6961,7 +7113,7 @@ export struct LocalMusic {
|
|
|
if (item.pixelMapPath) {
|
|
if (item.pixelMapPath) {
|
|
|
this.currentTitleCover = item.pixelMapPath
|
|
this.currentTitleCover = item.pixelMapPath
|
|
|
}
|
|
}
|
|
|
- this.updateListData(mList, true) // 使用 true 跳过排序
|
|
|
|
|
|
|
+ this.updateListData(mList, true, true) // 详情页立即替换列表,避免旧列表顶部闪现
|
|
|
} else {
|
|
} else {
|
|
|
ToastUtil.showToast('未找到该艺术家的歌曲');
|
|
ToastUtil.showToast('未找到该艺术家的歌曲');
|
|
|
return;
|
|
return;
|
|
@@ -7060,7 +7212,7 @@ export struct LocalMusic {
|
|
|
if (item.pixelMapPath) {
|
|
if (item.pixelMapPath) {
|
|
|
this.currentTitleCover = item.pixelMapPath
|
|
this.currentTitleCover = item.pixelMapPath
|
|
|
}
|
|
}
|
|
|
- this.updateListData(albumList, true) // 使用 true 跳过排序
|
|
|
|
|
|
|
+ this.updateListData(albumList, true, true) // 详情页立即替换列表,避免旧列表顶部闪现
|
|
|
this.mScrollMap.set(item.name, this.selectedIndex)
|
|
this.mScrollMap.set(item.name, this.selectedIndex)
|
|
|
this.rightTopImage = $r('sys.symbol.chevron_left')
|
|
this.rightTopImage = $r('sys.symbol.chevron_left')
|
|
|
} else {
|
|
} else {
|
|
@@ -7527,6 +7679,7 @@ export struct LocalMusic {
|
|
|
// 1. 检查缓存
|
|
// 1. 检查缓存
|
|
|
if (this.pageCache.has(pageIndex)) {
|
|
if (this.pageCache.has(pageIndex)) {
|
|
|
Logger.info('heanup LocalMusic', `loadPage: 从缓存加载 page=${pageIndex}`);
|
|
Logger.info('heanup LocalMusic', `loadPage: 从缓存加载 page=${pageIndex}`);
|
|
|
|
|
+ const inMediaLibraryWarmup = this.modeType === 1 && pageIndex === 0 && !append && this.isMediaLibraryWarmupWindow();
|
|
|
const cachedData = this.pageCache.get(pageIndex)!;
|
|
const cachedData = this.pageCache.get(pageIndex)!;
|
|
|
const cachedHasMore = this.pageHasMoreCache.get(pageIndex);
|
|
const cachedHasMore = this.pageHasMoreCache.get(pageIndex);
|
|
|
const cachedTotal = this.pageTotalCountCache.get(pageIndex);
|
|
const cachedTotal = this.pageTotalCountCache.get(pageIndex);
|
|
@@ -7546,13 +7699,33 @@ export struct LocalMusic {
|
|
|
} else {
|
|
} else {
|
|
|
this.videoLocalList = cachedData;
|
|
this.videoLocalList = cachedData;
|
|
|
this.dataSource.pushArrayData(this.videoLocalList);
|
|
this.dataSource.pushArrayData(this.videoLocalList);
|
|
|
- this.prefetchListThumbsFromItems(cachedData, 30);
|
|
|
|
|
- this.prefetchGridWaterThumbsFromItems(cachedData, this.twoFingerType == 4 ? 56 : 42);
|
|
|
|
|
|
|
+ if (inMediaLibraryWarmup) {
|
|
|
|
|
+ this.prefetchListThumbsFromItems(cachedData, 8);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.prefetchListThumbsFromItems(cachedData, 30);
|
|
|
|
|
+ this.prefetchGridWaterThumbsFromItems(cachedData, this.twoFingerType == 4 ? 56 : 42);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
this.setButtonStatus();
|
|
this.setButtonStatus();
|
|
|
- this.markAlphaBetDirty();
|
|
|
|
|
|
|
+ if (inMediaLibraryWarmup) {
|
|
|
|
|
+ this.scheduleAlphaBetRebuild(900);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.markAlphaBetDirty();
|
|
|
|
|
+ }
|
|
|
if (this.hasMoreData) {
|
|
if (this.hasMoreData) {
|
|
|
- this.prefetchFollowingPages(pageIndex + 1);
|
|
|
|
|
|
|
+ if (inMediaLibraryWarmup) {
|
|
|
|
|
+ if (this.mediaLibraryWarmupPrefetchTimer) {
|
|
|
|
|
+ clearTimeout(this.mediaLibraryWarmupPrefetchTimer);
|
|
|
|
|
+ }
|
|
|
|
|
+ this.mediaLibraryWarmupPrefetchTimer = setTimeout(() => {
|
|
|
|
|
+ this.mediaLibraryWarmupPrefetchTimer = 0;
|
|
|
|
|
+ if (this.modeType === 1 && !this.isCanBack) {
|
|
|
|
|
+ this.prefetchFollowingPages(pageIndex + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 900) as number;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.prefetchFollowingPages(pageIndex + 1);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -7636,6 +7809,7 @@ export struct LocalMusic {
|
|
|
this.cleanOldCache(pageIndex);
|
|
this.cleanOldCache(pageIndex);
|
|
|
|
|
|
|
|
// 5. 更新显示列表
|
|
// 5. 更新显示列表
|
|
|
|
|
+ const inMediaLibraryWarmup = this.modeType === 1 && pageIndex === 0 && !append && this.isMediaLibraryWarmupWindow();
|
|
|
if (append) {
|
|
if (append) {
|
|
|
this.prefetchListThumbsFromItems(result.items, 26);
|
|
this.prefetchListThumbsFromItems(result.items, 26);
|
|
|
this.prefetchGridWaterThumbsFromItems(result.items, this.twoFingerType == 4 ? 56 : 42);
|
|
this.prefetchGridWaterThumbsFromItems(result.items, this.twoFingerType == 4 ? 56 : 42);
|
|
@@ -7643,12 +7817,32 @@ export struct LocalMusic {
|
|
|
} else {
|
|
} else {
|
|
|
this.videoLocalList = result.items;
|
|
this.videoLocalList = result.items;
|
|
|
this.dataSource.pushArrayData(this.videoLocalList);
|
|
this.dataSource.pushArrayData(this.videoLocalList);
|
|
|
- this.prefetchListThumbsFromItems(result.items, 30);
|
|
|
|
|
- this.prefetchGridWaterThumbsFromItems(result.items, this.twoFingerType == 4 ? 56 : 42);
|
|
|
|
|
|
|
+ if (inMediaLibraryWarmup) {
|
|
|
|
|
+ this.prefetchListThumbsFromItems(result.items, 8);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.prefetchListThumbsFromItems(result.items, 30);
|
|
|
|
|
+ this.prefetchGridWaterThumbsFromItems(result.items, this.twoFingerType == 4 ? 56 : 42);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
this.setButtonStatus();
|
|
this.setButtonStatus();
|
|
|
- this.markAlphaBetDirty();
|
|
|
|
|
- this.prefetchFollowingPages(pageIndex + 1);
|
|
|
|
|
|
|
+ if (inMediaLibraryWarmup) {
|
|
|
|
|
+ this.scheduleAlphaBetRebuild(900);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.markAlphaBetDirty();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (inMediaLibraryWarmup) {
|
|
|
|
|
+ if (this.mediaLibraryWarmupPrefetchTimer) {
|
|
|
|
|
+ clearTimeout(this.mediaLibraryWarmupPrefetchTimer);
|
|
|
|
|
+ }
|
|
|
|
|
+ this.mediaLibraryWarmupPrefetchTimer = setTimeout(() => {
|
|
|
|
|
+ this.mediaLibraryWarmupPrefetchTimer = 0;
|
|
|
|
|
+ if (this.modeType === 1 && !this.isCanBack) {
|
|
|
|
|
+ this.prefetchFollowingPages(pageIndex + 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 900) as number;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.prefetchFollowingPages(pageIndex + 1);
|
|
|
|
|
+ }
|
|
|
this.isLoadingPage = false;
|
|
this.isLoadingPage = false;
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
const error = err as Error;
|
|
const error = err as Error;
|
|
@@ -7917,10 +8111,12 @@ export struct LocalMusic {
|
|
|
/**
|
|
/**
|
|
|
* 重置分页状态并加载首页
|
|
* 重置分页状态并加载首页
|
|
|
*/
|
|
*/
|
|
|
- private async resetAndLoadFirstPage(): Promise<void> {
|
|
|
|
|
|
|
+ private async resetAndLoadFirstPage(clearExistingList: boolean = true): Promise<void> {
|
|
|
Logger.info('heanup LocalMusic', 'resetAndLoadFirstPage: 重置分页状态');
|
|
Logger.info('heanup LocalMusic', 'resetAndLoadFirstPage: 重置分页状态');
|
|
|
this.currentPage = 0;
|
|
this.currentPage = 0;
|
|
|
- this.videoLocalList = [];
|
|
|
|
|
|
|
+ if (clearExistingList) {
|
|
|
|
|
+ this.videoLocalList = [];
|
|
|
|
|
+ }
|
|
|
this.pageCache.clear();
|
|
this.pageCache.clear();
|
|
|
this.pageHasMoreCache.clear();
|
|
this.pageHasMoreCache.clear();
|
|
|
this.pageTotalCountCache.clear();
|
|
this.pageTotalCountCache.clear();
|
|
@@ -8032,6 +8228,16 @@ export struct LocalMusic {
|
|
|
return this.modeType === 1 && !this.isGridMusic && this.twoFingerType != 4;
|
|
return this.modeType === 1 && !this.isGridMusic && this.twoFingerType != 4;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private isMediaLibraryWarmupWindow(): boolean {
|
|
|
|
|
+ if (this.modeType !== 1 || this.isCanBack) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.mediaLibraryEnterTs <= 0) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ return Date.now() - this.mediaLibraryEnterTs < 1800;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private getDynamicPreloadStartRatio(): number {
|
|
private getDynamicPreloadStartRatio(): number {
|
|
|
if (this.twoFingerType == 4) {
|
|
if (this.twoFingerType == 4) {
|
|
|
return 0.10;
|
|
return 0.10;
|
|
@@ -8078,6 +8284,19 @@ export struct LocalMusic {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private scrollToTopForCurrentLayout(): void {
|
|
|
|
|
+ if (this.twoFingerType == 4) {
|
|
|
|
|
+ this.waterScroller.scrollEdge(Edge.Top);
|
|
|
|
|
+ this.scroller.scrollEdge(Edge.Top);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.isGridMusic) {
|
|
|
|
|
+ this.scroller.scrollEdge(Edge.Top);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.listScroller.scrollEdge(Edge.Top);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private getListImageSource(item: VideoItem, index: number, coverVersion: number = 0): ResourceStr {
|
|
private getListImageSource(item: VideoItem, index: number, coverVersion: number = 0): ResourceStr {
|
|
|
const fallback = item.type == CommonConstants.TYPE_IS_DIR ? $r('app.media.dir_alt') : $r('app.media.alt');
|
|
const fallback = item.type == CommonConstants.TYPE_IS_DIR ? $r('app.media.dir_alt') : $r('app.media.alt');
|
|
|
if (coverVersion < -1) {
|
|
if (coverVersion < -1) {
|
|
@@ -12153,6 +12372,16 @@ export struct LocalMusic {
|
|
|
.geometryTransition('cover') // 绑定标识符
|
|
.geometryTransition('cover') // 绑定标识符
|
|
|
.opacity(this.opacityValueImage)
|
|
.opacity(this.opacityValueImage)
|
|
|
.clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.8 })
|
|
.clickEffect({ level: ClickEffectLevel.MIDDLE,scale:0.8 })
|
|
|
|
|
+ .bindContextMenu(this.MenuImageBuilder(this.cover), ResponseType.LongPress,
|
|
|
|
|
+ {
|
|
|
|
|
+ preview: MenuPreviewMode.IMAGE,
|
|
|
|
|
+ previewAnimationOptions: { scale: [0.8, 1.0] },
|
|
|
|
|
+ })
|
|
|
|
|
+ .bindContextMenu(this.MenuImageBuilder(this.cover), ResponseType.RightClick,
|
|
|
|
|
+ {
|
|
|
|
|
+ preview: MenuPreviewMode.IMAGE,
|
|
|
|
|
+ previewAnimationOptions: { scale: [0.8, 1.0] },
|
|
|
|
|
+ })
|
|
|
.visibility(this.isPuraWP() ? Visibility.None : Visibility.Visible)
|
|
.visibility(this.isPuraWP() ? Visibility.None : Visibility.Visible)
|
|
|
.borderRadius(this.isCoverRectangle ? 20 : '100%')
|
|
.borderRadius(this.isCoverRectangle ? 20 : '100%')
|
|
|
.clip(true)
|
|
.clip(true)
|
|
@@ -12262,6 +12491,39 @@ export struct LocalMusic {
|
|
|
centerY: '100%' })
|
|
centerY: '100%' })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Builder
|
|
|
|
|
+ MenuImageBuilder(cover: string|undefined) {
|
|
|
|
|
+ Menu(){
|
|
|
|
|
+ MenuItem({
|
|
|
|
|
+ symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.square_and_pencil')),
|
|
|
|
|
+ content: '设为壁纸'
|
|
|
|
|
+ })
|
|
|
|
|
+ .onClick(async() => {
|
|
|
|
|
+ if (StrUtil.isEmpty(cover)) {
|
|
|
|
|
+ ToastUtil.showToast('当前没有可用封面')
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.getUIContext().getRouter().pushUrl({
|
|
|
|
|
+ url: 'pages/WallpaperPreviewPage',
|
|
|
|
|
+ params: {
|
|
|
|
|
+ imageUri: cover
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ MenuItem({
|
|
|
|
|
+ symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.arrow_up_to_line')),
|
|
|
|
|
+ content: '更换封面'
|
|
|
|
|
+ })
|
|
|
|
|
+ .onClick(async() => {
|
|
|
|
|
+ await this.replaceCurrentSongCoverByAlbum();
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@Builder
|
|
@Builder
|
|
|
private musicNameInfo(isHidden: boolean) {
|
|
private musicNameInfo(isHidden: boolean) {
|
|
|
Row(){
|
|
Row(){
|
|
@@ -15216,7 +15478,7 @@ export struct LocalMusic {
|
|
|
let timeoutPlay = 0
|
|
let timeoutPlay = 0
|
|
|
if (StrUtil.isNotEmpty(this.videoUrl) &&
|
|
if (StrUtil.isNotEmpty(this.videoUrl) &&
|
|
|
(this.videoUrl.toLowerCase().endsWith('.rmvb') || this.videoUrl.toLowerCase().endsWith('.flac'))) {
|
|
(this.videoUrl.toLowerCase().endsWith('.rmvb') || this.videoUrl.toLowerCase().endsWith('.flac'))) {
|
|
|
- timeoutPlay = 300
|
|
|
|
|
|
|
+ timeoutPlay = 400
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//跳过片头片尾关键代码,以下是跳过片头的关键代码
|
|
//跳过片头片尾关键代码,以下是跳过片头的关键代码
|
|
@@ -17819,6 +18081,33 @@ function cutPopupBuilder(dataBu: BubbleBean) {
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+@Concurrent
|
|
|
|
|
+function buildAlphabetIndexTask(items: Array<AlphaTaskItem>, sortType: number, isShowFileName: boolean): Array<string> {
|
|
|
|
|
+ const alphabetSet = new Set<string>()
|
|
|
|
|
+ for (const item of items) {
|
|
|
|
|
+ let text = isShowFileName ? (item.fileName || '') :
|
|
|
|
|
+ (item.type != CommonConstants.TYPE_IS_DIR && item.pyStr) ? item.pyStr : (item.name || '')
|
|
|
|
|
+ if (sortType === 0 || sortType == 1) {
|
|
|
|
|
+ text = item.artist || ''
|
|
|
|
|
+ } else if (sortType === 2 || sortType == 3) {
|
|
|
|
|
+ text = item.album || ''
|
|
|
|
|
+ }
|
|
|
|
|
+ if (text && text.length > 0) {
|
|
|
|
|
+ const first = getFirstLetter(text)
|
|
|
|
|
+ if (!alphabetSet.has(first)) {
|
|
|
|
|
+ alphabetSet.add(first)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ const sorted = Array.from(alphabetSet).sort((a, b) => {
|
|
|
|
|
+ if (sortType === 4 || sortType == 0 || sortType == 2 || sortType == 6) {
|
|
|
|
|
+ return a.localeCompare(b)
|
|
|
|
|
+ }
|
|
|
|
|
+ return b.localeCompare(a)
|
|
|
|
|
+ })
|
|
|
|
|
+ return sorted
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
|
|
|
function getFileDirName(filePath: string,rootPath:string): string{
|
|
function getFileDirName(filePath: string,rootPath:string): string{
|
|
|
if(filePath===rootPath){
|
|
if(filePath===rootPath){
|
|
@@ -17915,3 +18204,29 @@ async function updateInfoDb(
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+@Concurrent
|
|
|
|
|
+async function replaceLocalMusicCoverTask(
|
|
|
|
|
+ context: Context,
|
|
|
|
|
+ filePath: string,
|
|
|
|
|
+ sandboxCoverPath: string
|
|
|
|
|
+): Promise<boolean> {
|
|
|
|
|
+ const coverResult: boolean = await changeMusicCover(context, filePath, sandboxCoverPath, true, '');
|
|
|
|
|
+ if (!coverResult) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const table: MediaTable = new MediaTable(context);
|
|
|
|
|
+ await new Promise<void>((resolve, reject) => {
|
|
|
|
|
+ table.getRdbStore(context, (err: Error) => {
|
|
|
|
|
+ err ? reject(err) : resolve();
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return await new Promise<boolean>((resolve) => {
|
|
|
|
|
+ table.updatePixelMapPath(filePath, fileUri.getUriFromPath(sandboxCoverPath),
|
|
|
|
|
+ (success: boolean, _error?: string) => {
|
|
|
|
|
+ resolve(success);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+}
|