diff --git a/android/app/src/main/java/cn/toside/music/mobile/utils/Utils.java b/android/app/src/main/java/cn/toside/music/mobile/utils/Utils.java index 6b2e42c..d3e6ec8 100644 --- a/android/app/src/main/java/cn/toside/music/mobile/utils/Utils.java +++ b/android/app/src/main/java/cn/toside/music/mobile/utils/Utils.java @@ -1,6 +1,11 @@ package cn.toside.music.mobile.utils; +import android.content.Context; +import android.os.storage.StorageManager; + +import com.facebook.react.bridge.ReactApplicationContext; + import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -8,6 +13,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.lang.reflect.Array; +import java.lang.reflect.Method; import java.util.concurrent.Callable; public class Utils { @@ -26,6 +33,31 @@ public class Utils { return dir.delete(); } + // https://gist.github.com/PauloLuan/4bcecc086095bce28e22?permalink_comment_id=2591001#gistcomment-2591001 + public static String getExternalStoragePath(ReactApplicationContext mContext, boolean is_removable) { + StorageManager mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); + Class storageVolumeClazz; + try { + storageVolumeClazz = Class.forName("android.os.storage.StorageVolume"); + Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList"); + Method getPath = storageVolumeClazz.getMethod("getPath"); + Method isRemovable = storageVolumeClazz.getMethod("isRemovable"); + Object result = getVolumeList.invoke(mStorageManager); + final int length = Array.getLength(result); + for (int i = 0; i < length; i++) { + Object storageVolumeElement = Array.get(result, i); + String path = (String) getPath.invoke(storageVolumeElement); + boolean removable = (Boolean) isRemovable.invoke(storageVolumeElement); + if (is_removable == removable) { + return path; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + public static String convertStreamToString(InputStream is) throws Exception { BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder sb = new StringBuilder(); diff --git a/android/app/src/main/java/cn/toside/music/mobile/utils/UtilsModule.java b/android/app/src/main/java/cn/toside/music/mobile/utils/UtilsModule.java index 8999e5f..2d46d48 100644 --- a/android/app/src/main/java/cn/toside/music/mobile/utils/UtilsModule.java +++ b/android/app/src/main/java/cn/toside/music/mobile/utils/UtilsModule.java @@ -395,5 +395,10 @@ public class UtilsModule extends ReactContextBaseJavaModule { public void requestIgnoreBatteryOptimization(Promise promise) { promise.resolve(BatteryOptimizationUtil.requestIgnoreBatteryOptimization(reactContext.getApplicationContext(), reactContext.getPackageName())); } + + @ReactMethod + public void getExternalStoragePath(Promise promise) { + promise.resolve(Utils.getExternalStoragePath(reactContext, true)); + } } diff --git a/publish/changeLog.md b/publish/changeLog.md index f990dc0..7d3078c 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -14,7 +14,7 @@ - 优化播放详情页歌曲封面、控制按钮对各尺寸屏幕的适配,修改横屏下的控制栏按钮布局 - 优化横竖屏界面的暂时判断,现在趋于方屏的屏幕按竖屏的方式显示,横屏下的播放栏添加上一曲切歌按钮 - 添加对wy源某些歌曲有问题的歌词进行修复(#370) -- 文件选择器允许(在旧系统)选择外置存储设备上的路径(`/storage`) +- 文件选择器允许(在旧系统)选择外置存储设备上的路径 - 图片显示改用第三方的图片组件,支持gif类型的图片显示,尝试解决某些设备上图片过多导致的应用崩溃问题 - 歌曲评论内容过长时自动折叠,需手动展开 - 改进本地音乐在线信息的匹配机制 diff --git a/src/components/MetadataEditModal/index.tsx b/src/components/MetadataEditModal/index.tsx index 50e0e60..f981be2 100644 --- a/src/components/MetadataEditModal/index.tsx +++ b/src/components/MetadataEditModal/index.tsx @@ -44,7 +44,7 @@ export default forwardRef((props, ref) => { readLyric(filePath), ]).then(([_metadata, pic, lyric]) => { if (!_metadata) return - if (isUnmounted) return + if (isUnmounted.current) return metadata.current = { name: _metadata.name, singer: _metadata.singer, diff --git a/src/components/common/ChoosePath/List.tsx b/src/components/common/ChoosePath/List.tsx index 032e533..d20094b 100644 --- a/src/components/common/ChoosePath/List.tsx +++ b/src/components/common/ChoosePath/List.tsx @@ -126,6 +126,7 @@ export default forwardRef(({ // console.log('prevPath', prevPath) // if (isReadingDir.current) return // setPath(prevPath) + throw err }).finally(() => { isReadingDir.current = false }) @@ -134,7 +135,7 @@ export default forwardRef(({ const onSetPath = (pathInfo: PathItem) => { // console.log('onSetPath') if (pathInfo.isDir) { - void readDir(pathInfo.path, readOptions.current.dirOnly, readOptions.current.filter) + void readDir(pathInfo.path, readOptions.current.dirOnly, readOptions.current.filter).catch(_ => _) } else { onConfirm(pathInfo.path) // setPath(pathInfo.path) @@ -143,7 +144,9 @@ export default forwardRef(({ const toParentDir = () => { const parentPath = path.substring(0, path.lastIndexOf('/')) - void readDir(parentPath.length ? parentPath : externalStorageDirectoryPath, readOptions.current.dirOnly, readOptions.current.filter) + void readDir(parentPath.length ? parentPath : externalStorageDirectoryPath, readOptions.current.dirOnly, readOptions.current.filter).catch(() => { + void readDir(externalStorageDirectoryPath, readOptions.current.dirOnly, readOptions.current.filter).catch(_ => _) + }) } const handleHide = () => { diff --git a/src/components/common/ChoosePath/components/Header.tsx b/src/components/common/ChoosePath/components/Header.tsx index 4a2dbb3..797cabf 100644 --- a/src/components/common/ChoosePath/components/Header.tsx +++ b/src/components/common/ChoosePath/components/Header.tsx @@ -1,4 +1,4 @@ -import { forwardRef, memo, useEffect, useImperativeHandle, useRef, useState } from 'react' +import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react' import { View, TouchableOpacity } from 'react-native' import Input, { type InputType } from '@/components/common/Input' import Text from '@/components/common/Text' @@ -9,6 +9,8 @@ import { createStyle, toast } from '@/utils/tools' import { mkdir, readDir } from '@/utils/fs' import { useTheme } from '@/store/theme/hook' import { scaleSizeH } from '@/utils/pixelRatio' +import { getExternalStoragePath } from '@/utils/nativeModules/utils' +import { useUnmounted } from '@/utils/hooks' const filterFileName = /[\\/:*?#"<>|]/ @@ -45,8 +47,6 @@ const NameInput = forwardRef((props, ref) => { ) }) -const storagePath = '/storage' - export default memo(({ title, path, @@ -59,26 +59,34 @@ export default memo(({ const theme = useTheme() const confirmAlertRef = useRef(null) const nameInputRef = useRef(null) + const storagePathRef = useRef('') const [isShowStorage, setIsShowStorage] = useState(false) + const isUnmounted = useUnmounted() + const checkExternalStoragePath = useCallback(() => { + void getExternalStoragePath().then((storagePath) => { + if (storagePath) { + void readDir(storagePath).then(() => { + if (isUnmounted.current) return + storagePathRef.current = storagePath + setIsShowStorage(true) + }).catch(() => { + setIsShowStorage(false) + }) + } else setIsShowStorage(false) + }) + }, []) useEffect(() => { - let isUnmounted = false - void readDir(storagePath).then(() => { - if (isUnmounted) return - setIsShowStorage(true) - }).catch(_ => _) - - return () => { - isUnmounted = true - } + checkExternalStoragePath() }, []) const refresh = () => { void onRefreshDir(path) + checkExternalStoragePath() } const toggleStorageDir = () => { - void onRefreshDir(storagePath) + void onRefreshDir(storagePathRef.current) } const handleShow = () => { diff --git a/src/utils/hooks/useUnmounted.tsx b/src/utils/hooks/useUnmounted.tsx index 38d2489..3c463cf 100644 --- a/src/utils/hooks/useUnmounted.tsx +++ b/src/utils/hooks/useUnmounted.tsx @@ -9,5 +9,5 @@ export function useUnmounted() { } }, []) - return isUnmountedRef.current + return isUnmountedRef } diff --git a/src/utils/nativeModules/utils.ts b/src/utils/nativeModules/utils.ts index c4e877d..13a2c41 100644 --- a/src/utils/nativeModules/utils.ts +++ b/src/utils/nativeModules/utils.ts @@ -70,6 +70,10 @@ export const onScreenStateChange = (handler: (state: 'ON' | 'OFF') => void): () } } +export const getExternalStoragePath = async(): Promise => { + return UtilsModule.getExternalStoragePath() +} + export const getWindowSize = async(): Promise<{ width: number, height: number }> => { return UtilsModule.getWindowSize() }