Add file picker for tv

pull/589/head
FongMi 10 months ago
parent 2c6e56c574
commit 25c55dc72e
  1. 5
      app/src/leanback/AndroidManifest.xml
  2. 68
      app/src/leanback/java/com/fongmi/android/tv/ui/activity/PickerActivity.java
  3. 54
      app/src/leanback/java/com/fongmi/android/tv/ui/presenter/PickPresenter.java
  4. 10
      app/src/leanback/res/drawable/ic_picker_file.xml
  5. 10
      app/src/leanback/res/drawable/ic_picker_folder.xml
  6. 36
      app/src/leanback/res/layout/adapter_picker.xml
  7. 10
      app/src/main/java/com/fongmi/android/tv/server/process/Local.java
  8. 24
      app/src/main/java/com/fongmi/android/tv/utils/FileChooser.java
  9. 8
      app/src/main/java/com/fongmi/android/tv/utils/FileUtil.java
  10. 35
      app/src/main/java/com/fongmi/android/tv/utils/Util.java
  11. 12
      app/src/main/res/layout/activity_picker.xml
  12. 13
      catvod/src/main/java/com/github/catvod/utils/Path.java

@ -135,6 +135,11 @@
android:launchMode="singleTop"
android:screenOrientation="sensorLandscape" />
<activity
android:name=".ui.activity.PickerActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:screenOrientation="sensorLandscape" />
<activity
android:name=".ui.activity.SettingActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"

@ -0,0 +1,68 @@
package com.fongmi.android.tv.ui.activity;
import android.content.Intent;
import android.net.Uri;
import androidx.leanback.widget.ArrayObjectAdapter;
import androidx.leanback.widget.ItemBridgeAdapter;
import androidx.viewbinding.ViewBinding;
import com.fongmi.android.tv.databinding.ActivityPickerBinding;
import com.fongmi.android.tv.ui.base.BaseActivity;
import com.fongmi.android.tv.ui.presenter.PickPresenter;
import com.fongmi.android.tv.utils.ResUtil;
import com.github.catvod.utils.Path;
import java.io.File;
public class PickerActivity extends BaseActivity implements PickPresenter.OnClickListener {
private ActivityPickerBinding mBinding;
private ArrayObjectAdapter mAdapter;
private File dir;
private boolean isRoot() {
return Path.root().equals(dir);
}
@Override
protected ViewBinding getBinding() {
return mBinding = ActivityPickerBinding.inflate(getLayoutInflater());
}
@Override
protected void initView() {
setRecyclerView();
update(Path.root());
}
private void setRecyclerView() {
mBinding.recycler.setHasFixedSize(true);
mBinding.recycler.setVerticalSpacing(ResUtil.dp2px(16));
mBinding.recycler.setAdapter(new ItemBridgeAdapter(mAdapter = new ArrayObjectAdapter(new PickPresenter(this))));
}
private void update(File dir) {
mBinding.recycler.setSelectedPosition(0);
mAdapter.setItems(Path.list(this.dir = dir), null);
}
@Override
public void onItemClick(File file) {
if (file.isDirectory()) {
update(file);
} else {
setResult(RESULT_OK, new Intent().setData(Uri.fromFile(file)));
finish();
}
}
@Override
public void onBackPressed() {
if (isRoot()) {
super.onBackPressed();
} else {
update(dir.getParentFile());
}
}
}

@ -0,0 +1,54 @@
package com.fongmi.android.tv.ui.presenter;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.leanback.widget.Presenter;
import com.fongmi.android.tv.R;
import com.fongmi.android.tv.databinding.AdapterPickerBinding;
import java.io.File;
public class PickPresenter extends Presenter {
private final OnClickListener mListener;
public PickPresenter(OnClickListener listener) {
this.mListener = listener;
}
public interface OnClickListener {
void onItemClick(File file);
}
@Override
public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
return new ViewHolder(AdapterPickerBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
}
@Override
public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object object) {
File file = (File) object;
ViewHolder holder = (ViewHolder) viewHolder;
holder.binding.name.setText(file.getName());
holder.binding.getRoot().setOnClickListener(v -> 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;
}
}
}

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/white"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M320,720L640,720L640,640L320,640L320,720ZM320,560L640,560L640,480L320,480L320,560ZM240,880Q207,880 183.5,856.5Q160,833 160,800L160,160Q160,127 183.5,103.5Q207,80 240,80L560,80L800,320L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM520,360L720,360L520,160L520,360Z" />
</vector>

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="@color/amber_300"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M160,800Q127,800 103.5,776.5Q80,753 80,720L80,240Q80,207 103.5,183.5Q127,160 160,160L400,160L480,240L800,240Q833,240 856.5,263.5Q880,287 880,320L880,720Q880,753 856.5,776.5Q833,800 800,800L160,800Z" />
</vector>

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/shape_vod_list"
android:focusable="true"
android:focusableInTouchMode="true"
android:foreground="@drawable/selector_vod">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="16dp"
android:scaleType="fitCenter"
tools:src="@drawable/ic_picker_folder" />
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:singleLine="true"
android:textColor="@color/white"
android:textSize="16sp"
tools:text="Download" />
</LinearLayout>
</FrameLayout>

@ -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<File> 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) {

@ -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) {

@ -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;
}

@ -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;
}
}

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.leanback.widget.VerticalGridView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp" />
</FrameLayout>

@ -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<File> 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) {

Loading…
Cancel
Save