diff --git a/.gitignore b/.gitignore index 4e9a1c8aa..8591abf69 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,4 @@ *build *.jks /local.properties -/media -*ser* \ No newline at end of file +/media \ No newline at end of file diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/MainActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/MainActivity.java index e6f18075a..c6ed60aa0 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/MainActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/MainActivity.java @@ -20,10 +20,10 @@ import com.fongmi.android.tv.event.RefreshEvent; import com.fongmi.android.tv.net.Callback; import com.fongmi.android.tv.server.Server; import com.fongmi.android.tv.ui.base.BaseActivity; +import com.fongmi.android.tv.ui.custom.FileChooser; import com.fongmi.android.tv.ui.custom.FragmentStateManager; import com.fongmi.android.tv.ui.fragment.SettingFragment; import com.fongmi.android.tv.ui.fragment.VodFragment; -import com.fongmi.android.tv.utils.FileChooser; import com.fongmi.android.tv.utils.Notify; import com.google.android.material.navigation.NavigationBarView; diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/FileChooser.java b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/FileChooser.java new file mode 100644 index 000000000..4077d31ff --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/FileChooser.java @@ -0,0 +1,183 @@ +package com.fongmi.android.tv.ui.custom; + +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.provider.MediaStore; + +import androidx.fragment.app.Fragment; + +import com.fongmi.android.tv.utils.FileUtil; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; + +public class FileChooser { + + public static final int REQUEST_PICK_FILE = 9999; + + private final Fragment fragment; + + public static FileChooser from(Fragment fragment) { + return new FileChooser(fragment); + } + + private FileChooser(Fragment fragment) { + this.fragment = fragment; + } + + public void show() { + show("*/*"); + } + + public void show(String mimeType) { + show(mimeType, REQUEST_PICK_FILE); + } + + public void show(String mimeType, int code) { + String[] mimeTypes = mimeType.split(" "); + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.setType(mimeTypes[0]); + intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); + intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false); + Intent destIntent = Intent.createChooser(intent, ""); + if (fragment != null) fragment.startActivityForResult(destIntent, code); + } + + public static String getPathFromUri(Context context, Uri uri) { + if (uri == null) return null; + String path = null; + if (DocumentsContract.isDocumentUri(context, uri)) path = getPathFromDocumentUri(context, uri); + else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) path = getDataColumn(context, uri); + else if (ContentResolver.SCHEME_FILE.equalsIgnoreCase(uri.getScheme())) path = uri.getPath(); + return path != null ? path : createFileFromUri(context, uri).getAbsolutePath(); + } + + private static String getPathFromDocumentUri(Context context, Uri uri) { + String docId = DocumentsContract.getDocumentId(uri); + String[] split = docId.split(":"); + if (isExternalStorageDocument(uri)) return getPath(docId, split); + else if (isDownloadsDocument(uri)) return getPath(context, uri, docId); + else if (isMediaDocument(uri)) return getPath(context, split); + else return null; + } + + private static String getPath(String docId, String[] split) { + if ("primary".equalsIgnoreCase(split[0])) { + return split.length > 1 ? Environment.getExternalStorageDirectory() + "/" + split[1] : Environment.getExternalStorageDirectory() + "/"; + } else { + return "/storage/" + docId.replace(":", "/"); + } + } + + private static String getPath(Context context, Uri uri, String docId) { + String fileName = getNameColumn(context, uri); + if (docId.startsWith("raw:")) { + return docId.replaceFirst("raw:", ""); + } else if (fileName != null) { + return Environment.getExternalStorageDirectory() + "/Download/" + fileName; + } else { + return getDataColumn(context, ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(docId))); + } + } + + private static String getPath(Context context, String[] split) { + switch (split[0]) { + case "image": + return getDataColumn(context, ContentUris.withAppendedId(getImageUri(), Long.parseLong(split[1]))); + case "video": + return getDataColumn(context, ContentUris.withAppendedId(getVideoUri(), Long.parseLong(split[1]))); + case "audio": + return getDataColumn(context, ContentUris.withAppendedId(getAudioUri(), Long.parseLong(split[1]))); + default: + return getDataColumn(context, ContentUris.withAppendedId(MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL), Long.parseLong(split[1]))); + } + } + + private static File createFileFromUri(Context context, Uri uri) { + String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME}; + Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null); + try (cursor) { + if (cursor == null || !cursor.moveToFirst()) return null; + InputStream is = context.getContentResolver().openInputStream(uri); + if (is == null) return null; + int count; + byte[] buffer = new byte[4096]; + int column = cursor.getColumnIndexOrThrow(projection[0]); + File file = new File(FileUtil.getCachePath(), cursor.getString(column)); + FileOutputStream os = new FileOutputStream(file); + while ((count = is.read(buffer)) != -1) os.write(buffer, 0, count); + os.close(); + is.close(); + return file; + } catch (Exception e) { + return null; + } + } + + private static String getDataColumn(Context context, Uri uri) { + String[] projection = {MediaStore.MediaColumns.DATA}; + Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null); + try (cursor) { + if (cursor == null || !cursor.moveToFirst()) return null; + return cursor.getString(cursor.getColumnIndexOrThrow(projection[0])); + } catch (Exception e) { + return null; + } + } + + private static String getNameColumn(Context context, Uri uri) { + String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME}; + Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null); + try (cursor) { + if (cursor == null || !cursor.moveToFirst()) return null; + return cursor.getString(cursor.getColumnIndexOrThrow(projection[0])); + } catch (Exception e) { + return null; + } + } + + private static Uri getImageUri() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL); + } else { + return MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } + } + + private static Uri getVideoUri() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL); + } else { + return MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + } + } + + private static Uri getAudioUri() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL); + } else { + return MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + } + } + + private static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + private static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + private static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } +} \ No newline at end of file diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ConfigDialog.java b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ConfigDialog.java index 91095c11b..b33d706d2 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ConfigDialog.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ConfigDialog.java @@ -15,7 +15,7 @@ import com.fongmi.android.tv.api.WallConfig; import com.fongmi.android.tv.bean.Config; import com.fongmi.android.tv.databinding.DialogConfigBinding; import com.fongmi.android.tv.impl.ConfigCallback; -import com.fongmi.android.tv.utils.FileChooser; +import com.fongmi.android.tv.ui.custom.FileChooser; import com.fongmi.android.tv.utils.Utils; import com.google.android.material.dialog.MaterialAlertDialogBuilder; diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/SettingFragment.java b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/SettingFragment.java index 444cbf868..0e9221843 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/SettingFragment.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/SettingFragment.java @@ -26,7 +26,7 @@ import com.fongmi.android.tv.impl.LiveCallback; import com.fongmi.android.tv.impl.SiteCallback; import com.fongmi.android.tv.net.Callback; import com.fongmi.android.tv.ui.base.BaseFragment; -import com.fongmi.android.tv.utils.FileChooser; +import com.fongmi.android.tv.ui.custom.FileChooser; import com.fongmi.android.tv.ui.custom.dialog.ConfigDialog; import com.fongmi.android.tv.ui.custom.dialog.HistoryDialog; import com.fongmi.android.tv.ui.custom.dialog.LiveDialog; diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/VodFragment.java b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/VodFragment.java index cf0614399..686425c0b 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/VodFragment.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/VodFragment.java @@ -35,11 +35,11 @@ import com.fongmi.android.tv.ui.activity.HistoryActivity; import com.fongmi.android.tv.ui.activity.KeepActivity; import com.fongmi.android.tv.ui.adapter.TypeAdapter; import com.fongmi.android.tv.ui.base.BaseFragment; +import com.fongmi.android.tv.ui.custom.FileChooser; import com.fongmi.android.tv.ui.custom.dialog.FilterDialog; import com.fongmi.android.tv.ui.custom.dialog.LinkDialog; import com.fongmi.android.tv.ui.custom.dialog.ReceiveDialog; import com.fongmi.android.tv.ui.custom.dialog.SiteDialog; -import com.fongmi.android.tv.utils.FileChooser; import com.fongmi.android.tv.utils.Prefers; import com.fongmi.android.tv.utils.Trans; import com.google.android.material.bottomsheet.BottomSheetDialogFragment;