From 25c55dc72eebe5be0adbb6287d9571fc465bbe5f Mon Sep 17 00:00:00 2001 From: FongMi Date: Sun, 23 Feb 2025 17:16:07 +0800 Subject: [PATCH] Add file picker for tv --- app/src/leanback/AndroidManifest.xml | 5 ++ .../tv/ui/activity/PickerActivity.java | 68 +++++++++++++++++++ .../tv/ui/presenter/PickPresenter.java | 54 +++++++++++++++ .../leanback/res/drawable/ic_picker_file.xml | 10 +++ .../res/drawable/ic_picker_folder.xml | 10 +++ .../leanback/res/layout/adapter_picker.xml | 36 ++++++++++ .../android/tv/server/process/Local.java | 10 +-- .../fongmi/android/tv/utils/FileChooser.java | 24 +++++-- .../com/fongmi/android/tv/utils/FileUtil.java | 8 +-- .../com/fongmi/android/tv/utils/Util.java | 35 ---------- app/src/main/res/layout/activity_picker.xml | 12 ++++ .../java/com/github/catvod/utils/Path.java | 13 +++- 12 files changed, 233 insertions(+), 52 deletions(-) create mode 100644 app/src/leanback/java/com/fongmi/android/tv/ui/activity/PickerActivity.java create mode 100644 app/src/leanback/java/com/fongmi/android/tv/ui/presenter/PickPresenter.java create mode 100644 app/src/leanback/res/drawable/ic_picker_file.xml create mode 100644 app/src/leanback/res/drawable/ic_picker_folder.xml create mode 100644 app/src/leanback/res/layout/adapter_picker.xml create mode 100644 app/src/main/res/layout/activity_picker.xml diff --git a/app/src/leanback/AndroidManifest.xml b/app/src/leanback/AndroidManifest.xml index 57b88e609..d684107c2 100644 --- a/app/src/leanback/AndroidManifest.xml +++ b/app/src/leanback/AndroidManifest.xml @@ -135,6 +135,11 @@ android:launchMode="singleTop" android:screenOrientation="sensorLandscape" /> + + mListener.onItemClick(file)); + holder.binding.image.setImageResource(file.isDirectory() ? R.drawable.ic_picker_folder : R.drawable.ic_picker_file); + } + + @Override + public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { + } + + public static class ViewHolder extends Presenter.ViewHolder { + + private final AdapterPickerBinding binding; + + public ViewHolder(@NonNull AdapterPickerBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + } +} \ No newline at end of file diff --git a/app/src/leanback/res/drawable/ic_picker_file.xml b/app/src/leanback/res/drawable/ic_picker_file.xml new file mode 100644 index 000000000..2f84c9580 --- /dev/null +++ b/app/src/leanback/res/drawable/ic_picker_file.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/leanback/res/drawable/ic_picker_folder.xml b/app/src/leanback/res/drawable/ic_picker_folder.xml new file mode 100644 index 000000000..2e938092f --- /dev/null +++ b/app/src/leanback/res/drawable/ic_picker_folder.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/leanback/res/layout/adapter_picker.xml b/app/src/leanback/res/layout/adapter_picker.xml new file mode 100644 index 000000000..d26575a08 --- /dev/null +++ b/app/src/leanback/res/layout/adapter_picker.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/fongmi/android/tv/server/process/Local.java b/app/src/main/java/com/fongmi/android/tv/server/process/Local.java index 2d5cb3bc2..96ebbb25b 100644 --- a/app/src/main/java/com/fongmi/android/tv/server/process/Local.java +++ b/app/src/main/java/com/fongmi/android/tv/server/process/Local.java @@ -16,8 +16,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.text.SimpleDateFormat; -import java.util.Arrays; import java.util.Date; +import java.util.List; import java.util.Locale; import java.util.Map; @@ -79,17 +79,13 @@ public class Local implements Process { } private Response getFolder(File root) { - File[] list = root.listFiles(); + List list = Path.list(root); JsonObject info = new JsonObject(); info.addProperty("parent", root.equals(Path.root()) ? "." : root.getParent().replace(Path.rootPath(), "")); - if (list == null || list.length == 0) { + if (list.isEmpty()) { info.add("files", new JsonArray()); return Nano.ok(info.toString()); } - Arrays.sort(list, (o1, o2) -> { - if (o1.isDirectory() && o2.isFile()) return -1; - return o1.isFile() && o2.isDirectory() ? 1 : o1.getName().compareTo(o2.getName()); - }); JsonArray files = new JsonArray(); info.add("files", files); for (File file : list) { diff --git a/app/src/main/java/com/fongmi/android/tv/utils/FileChooser.java b/app/src/main/java/com/fongmi/android/tv/utils/FileChooser.java index b84fef913..ed3f80ca8 100644 --- a/app/src/main/java/com/fongmi/android/tv/utils/FileChooser.java +++ b/app/src/main/java/com/fongmi/android/tv/utils/FileChooser.java @@ -1,5 +1,6 @@ package com.fongmi.android.tv.utils; +import android.app.Activity; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; @@ -14,6 +15,7 @@ import android.provider.MediaStore; import androidx.fragment.app.Fragment; import com.fongmi.android.tv.App; +import com.fongmi.android.tv.ui.activity.PickerActivity; import com.github.catvod.utils.Path; import java.io.File; @@ -24,12 +26,21 @@ public class FileChooser { public static final int REQUEST_PICK_FILE = 9999; - private final Fragment fragment; + private Activity activity; + private Fragment fragment; + + public static FileChooser from(Activity activity) { + return new FileChooser(activity); + } public static FileChooser from(Fragment fragment) { return new FileChooser(fragment); } + private FileChooser(Activity activity) { + this.activity = activity; + } + private FileChooser(Fragment fragment) { this.fragment = fragment; } @@ -47,14 +58,19 @@ public class FileChooser { } public void show(String mimeType, String[] mimeTypes, int code) { - Intent intent = new Intent(Util.isTvBox() ? Intent.ACTION_GET_CONTENT : Intent.ACTION_OPEN_DOCUMENT); + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.setType(mimeType); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false); intent.putExtra("android.content.extra.SHOW_ADVANCED", true); - if (intent.resolveActivity(App.get().getPackageManager()) == null) return; - if (fragment != null) fragment.startActivityForResult(Intent.createChooser(intent, ""), code); + if (intent.resolveActivity(App.get().getPackageManager()) == null) { + if (activity != null) activity.startActivityForResult(Intent.createChooser(intent, ""), code); + if (fragment != null) fragment.startActivityForResult(Intent.createChooser(intent, ""), code); + } else { + if (activity != null) activity.startActivityForResult(new Intent(activity, PickerActivity.class), code); + if (fragment != null) fragment.startActivityForResult(new Intent(fragment.getActivity(), PickerActivity.class), code); + } } public static boolean isValid(Context context, Uri uri) { diff --git a/app/src/main/java/com/fongmi/android/tv/utils/FileUtil.java b/app/src/main/java/com/fongmi/android/tv/utils/FileUtil.java index 4f8c21e9e..3deb9f327 100644 --- a/app/src/main/java/com/fongmi/android/tv/utils/FileUtil.java +++ b/app/src/main/java/com/fongmi/android/tv/utils/FileUtil.java @@ -91,11 +91,11 @@ public class FileUtil { }); } - public static long getDirectorySize(File file) { + public static long getDirectorySize(File dir) { long size = 0; - if (file == null) return 0; - if (file.isDirectory()) for (File f : Path.list(file)) size += getDirectorySize(f); - else size = file.length(); + if (dir == null) return 0; + if (dir.isDirectory()) for (File file: Path.list(dir)) size += getDirectorySize(file); + else size = dir.length(); return size; } diff --git a/app/src/main/java/com/fongmi/android/tv/utils/Util.java b/app/src/main/java/com/fongmi/android/tv/utils/Util.java index 3799a83c3..333b6e424 100644 --- a/app/src/main/java/com/fongmi/android/tv/utils/Util.java +++ b/app/src/main/java/com/fongmi/android/tv/utils/Util.java @@ -1,15 +1,12 @@ package com.fongmi.android.tv.utils; import android.app.Activity; -import android.app.UiModeManager; import android.content.ClipData; import android.content.ClipboardManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.res.Configuration; import android.os.Build; import android.os.IBinder; import android.os.Parcelable; @@ -171,36 +168,4 @@ public class Util { return Intent.createChooser(intent, null); } } - - public static boolean hasSAFChooser() { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("video/*"); - return intent.resolveActivity(App.get().getPackageManager()) != null; - } - - public static boolean isTvBox() { - PackageManager pm = App.get().getPackageManager(); - if (Configuration.UI_MODE_TYPE_TELEVISION == ((UiModeManager) App.get().getSystemService(Context.UI_MODE_SERVICE)).getCurrentModeType()) { - return true; - } - if (pm.hasSystemFeature("amazon.hardware.fire_tv")) { - return true; - } - if (!hasSAFChooser()) { - return true; - } - if (Build.VERSION.SDK_INT < 30) { - if (!pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN) && !pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { - return true; - } - if (pm.hasSystemFeature("android.hardware.hdmi.cec")) { - return true; - } - if (Build.MANUFACTURER.equalsIgnoreCase("zidoo")) { - return true; - } - } - return false; - } } diff --git a/app/src/main/res/layout/activity_picker.xml b/app/src/main/res/layout/activity_picker.xml new file mode 100644 index 000000000..7d8ce1464 --- /dev/null +++ b/app/src/main/res/layout/activity_picker.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/catvod/src/main/java/com/github/catvod/utils/Path.java b/catvod/src/main/java/com/github/catvod/utils/Path.java index 4ee01da16..3e473485a 100644 --- a/catvod/src/main/java/com/github/catvod/utils/Path.java +++ b/catvod/src/main/java/com/github/catvod/utils/Path.java @@ -11,8 +11,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; public class Path { @@ -196,9 +196,18 @@ public class Path { } } + public static void sort(File[] files) { + Arrays.sort(files, (o1, o2) -> { + if (o1.isDirectory() && o2.isFile()) return -1; + if (o1.isFile() && o2.isDirectory()) return 1; + return o1.getName().compareTo(o2.getName()); + }); + } + public static List list(File dir) { File[] files = dir.listFiles(); - return files == null ? Collections.emptyList() : Arrays.asList(files); + if (files != null) sort(files); + return files == null ? new ArrayList<>() : Arrays.asList(files); } public static void clear(File dir) {