diff --git a/.gitignore b/.gitignore index aa724b7..b9048a3 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ .externalNativeBuild .cxx local.properties +app/build diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..6863b14 --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 261e26d..e61186d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -31,7 +31,7 @@ android { dependencies { implementation fileTree(dir: "libs", includes: ["*.aar", "*.jar"]) //文件选择器(包含权限请求) - implementation 'io.github.molihuan:pathselector:1.1.5' + implementation 'io.github.molihuan:pathselector:1.1.6' //RxFFmpeg依赖 implementation 'com.github.microshow:RxFFmpeg:4.9.0-lite' //bugly依赖 @@ -47,6 +47,7 @@ dependencies { implementation 'com.github.bumptech.glide:glide:4.12.0' //XUI + implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'com.google.android.material:material:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' diff --git a/app/release/app-release.apk "b/app/release/1.6.0\345\260\235\351\262\234\347\211\210.apk" similarity index 90% rename from app/release/app-release.apk rename to "app/release/1.6.0\345\260\235\351\262\234\347\211\210.apk" index e3e7776..836c46a 100644 Binary files a/app/release/app-release.apk and "b/app/release/1.6.0\345\260\235\351\262\234\347\211\210.apk" differ diff --git a/app/src/main/java/com/molihua/hlbmerge/App.java b/app/src/main/java/com/molihua/hlbmerge/App.java index b8acbae..f5e38e9 100644 --- a/app/src/main/java/com/molihua/hlbmerge/App.java +++ b/app/src/main/java/com/molihua/hlbmerge/App.java @@ -4,6 +4,7 @@ import com.molihua.hlbmerge.dao.ConfigData; import com.molihuan.pathselector.PathSelector; +import com.molihuan.pathselector.configs.PathSelectorConfig; import com.tencent.bugly.Bugly; import com.tencent.mmkv.MMKV; import com.xuexiang.xui.XUI; @@ -32,6 +33,8 @@ public void onCreate() { ConfigData.init(); //路径选择器debug PathSelector.setDebug(true); + //取消自动申请权限 + PathSelectorConfig.setAutoGetPermission(false); super.onCreate(); } diff --git a/app/src/main/java/com/molihua/hlbmerge/activity/AbstractMainActivity.java b/app/src/main/java/com/molihua/hlbmerge/activity/AbstractMainActivity.java index 1262499..ab1e35a 100644 --- a/app/src/main/java/com/molihua/hlbmerge/activity/AbstractMainActivity.java +++ b/app/src/main/java/com/molihua/hlbmerge/activity/AbstractMainActivity.java @@ -4,6 +4,7 @@ import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.navigation.NavigationView; +import com.molihua.hlbmerge.fragment.AbstractMainFileShowFragment; import com.molihua.hlbmerge.interfaces.IMainFileShowFragment; import com.molihua.hlbmerge.interfaces.IMainTitlebarFragment; @@ -23,4 +24,6 @@ public abstract class AbstractMainActivity extends BaseActivity implements IMain public abstract void showHideNavigation(boolean status); public abstract void handleShowHide(boolean isShow); + + public abstract AbstractMainFileShowFragment getMainFileShowFragment(); } diff --git a/app/src/main/java/com/molihua/hlbmerge/activity/impl/MainActivity.java b/app/src/main/java/com/molihua/hlbmerge/activity/impl/MainActivity.java index 37846e4..8079b53 100644 --- a/app/src/main/java/com/molihua/hlbmerge/activity/impl/MainActivity.java +++ b/app/src/main/java/com/molihua/hlbmerge/activity/impl/MainActivity.java @@ -66,6 +66,8 @@ public void getComponents() { @Override public void initData() { + + //申请权限 PermissionsTools.getStoragePermissions(this); mainTitlebarFragment = new MainTitlebarFragment(); @@ -187,6 +189,11 @@ public NavigationView getNavigationView() { return navigationView; } + @Override + public AbstractMainFileShowFragment getMainFileShowFragment() { + return mainFileShowFragment; + } + @Override public void showHideNavigation(boolean status) { if (status) { diff --git a/app/src/main/java/com/molihua/hlbmerge/dialog/impl/MergeOptionDialog.java b/app/src/main/java/com/molihua/hlbmerge/dialog/impl/MergeOptionDialog.java index 6537dd6..b6a12dc 100644 --- a/app/src/main/java/com/molihua/hlbmerge/dialog/impl/MergeOptionDialog.java +++ b/app/src/main/java/com/molihua/hlbmerge/dialog/impl/MergeOptionDialog.java @@ -6,6 +6,7 @@ import android.widget.CompoundButton; import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import com.molihua.hlbmerge.R; import com.molihua.hlbmerge.dao.ConfigData; @@ -17,6 +18,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * @ClassName: JudgeMergeDialog @@ -26,14 +28,29 @@ */ public class MergeOptionDialog { - - public static MaterialDialog showMergeOptionDialog(CacheFile cacheFile, Context context) { + /** + * 单个合并 + * + * @param cacheFile + * @param fragment + * @return + */ + public static MaterialDialog showMergeOptionDialog(CacheFile cacheFile, Fragment fragment) { List cacheFileList = new ArrayList<>(); cacheFileList.add(cacheFile); - return showMergeOptionDialog(cacheFileList, context); + return showMergeOptionDialog(cacheFileList, fragment); } - public static MaterialDialog showMergeOptionDialog(List cacheFileList, Context context) { + /** + * 多个合并 + * + * @param cacheFileList + * @param fragment + * @return + */ + public static MaterialDialog showMergeOptionDialog(List cacheFileList, Fragment fragment) { + Context context = fragment.getContext(); + Objects.requireNonNull(context, "context is null"); View dialog_judgemerge = LayoutInflater.from(context).inflate(R.layout.dialog_judge_merge, null); MaterialSpinner dialog_materialspinner = dialog_judgemerge.findViewById(R.id.dialog_materialspinner); @@ -68,7 +85,7 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { //打开合并进度窗口 - MergeProgressDialog.showMergeProgressDialog(cacheFileList, context); + MergeProgressDialog.showMergeProgressDialog(cacheFileList, fragment); } }) .negativeText("取消") diff --git a/app/src/main/java/com/molihua/hlbmerge/dialog/impl/MergeProgressDialog.java b/app/src/main/java/com/molihua/hlbmerge/dialog/impl/MergeProgressDialog.java index 88bf8d5..7e5460a 100644 --- a/app/src/main/java/com/molihua/hlbmerge/dialog/impl/MergeProgressDialog.java +++ b/app/src/main/java/com/molihua/hlbmerge/dialog/impl/MergeProgressDialog.java @@ -2,14 +2,21 @@ import android.content.Context; import android.content.DialogInterface; +import android.net.Uri; import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import com.blankj.molihuan.utilcode.util.FileIOUtils; import com.blankj.molihuan.utilcode.util.FileUtils; +import com.blankj.molihuan.utilcode.util.UriUtils; import com.molihua.hlbmerge.dao.ConfigData; import com.molihua.hlbmerge.entity.CacheFile; -import com.molihua.hlbmerge.service.BaseCacheFileManager; +import com.molihua.hlbmerge.service.impl.PathCacheFileManager; import com.molihua.hlbmerge.service.impl.RxFFmpegCallback; +import com.molihua.hlbmerge.service.impl.UriCacheFileManager; +import com.molihua.hlbmerge.utils.RxFfmpegTools; +import com.molihuan.pathselector.utils.FileTools; import com.xuexiang.xtask.XTask; import com.xuexiang.xtask.core.ITaskChainEngine; import com.xuexiang.xtask.core.param.ITaskResult; @@ -21,8 +28,7 @@ import java.io.File; import java.util.List; - -import io.microshow.rxffmpeg.RxFFmpegInvoke; +import java.util.Objects; /** * @ClassName: MergeProgressDialog @@ -34,13 +40,29 @@ public class MergeProgressDialog { //用户是否选择的标志位 public static boolean FLAG_USER_HANDLE = false; - public static String cmdTemplate = "ffmpeg -i %s -i %s -c copy %s.mp4"; + public static final String CMD_TEMPLATE = "ffmpeg -i %s -i %s -c copy %s"; + /** + * 显示合并进度弹窗 + * + * @param cacheFileList + * @return + */ + public static MaterialDialog showMergeProgressDialog(List cacheFileList, Fragment fragment) { + Context context = fragment.getContext(); + Objects.requireNonNull(context, "context is null"); - public static MaterialDialog showMergeProgressDialog(List cacheFileList, Context context) { + List handledCacheFileList; + //是否需要使用uri + boolean dataUseUri = FileTools.underAndroidDataUseUri(ConfigData.getCacheFilePath()); //把合集item处理成章节item - List handledCacheFileList = BaseCacheFileManager.collection2ChapterCacheFileList(cacheFileList); + if (dataUseUri) { + handledCacheFileList = UriCacheFileManager.collection2ChapterCacheFileList(fragment, cacheFileList); + } else { + handledCacheFileList = PathCacheFileManager.collection2ChapterCacheFileList(cacheFileList); + } + MaterialDialog materialDialog = new MaterialDialog.Builder(context) .title("提示") @@ -77,53 +99,80 @@ public static void startMerge(List cacheFileList, MaterialDialog dial RxFFmpegCallback ffmpegCallback = new RxFFmpegCallback(dialog); //获取输出根目录 String outRoot = ConfigData.getOutputFilePath(); + //获取导出配置 + int exportType = ConfigData.getExportType(); + boolean exportDanmaku = ConfigData.isExportDanmaku(); XTask.getTaskChain() .addTask(XTask.getTask(new TaskCommand() { @Override public void run() throws Exception { - String[] cmd; String cmdStr; + CacheFile srcCacheFile; CacheFile cacheFile; String subOutPath; String completeOutPath; + String completeOutPathSuf; for (int i = 0; i < cacheFileList.size(); i++) { - cacheFile = cacheFileList.get(i); + srcCacheFile = cacheFileList.get(i); + + //将uri转换为file并把路径返回存入CacheFile中 + cacheFile = MergeProgressDialog.cacheFileUri2File(srcCacheFile); + //创建输出目录 subOutPath = outRoot + File.separator + cacheFile.getCollectionName(); FileUtils.createOrExistsDir(subOutPath); //获取完整的输出目录 completeOutPath = subOutPath + File.separator + cacheFile.getChapterName(); - //判断是否已经存在,如果存在则让用户选择都保留还是直接覆盖 - if (FileUtils.isFileExists(completeOutPath + ".mp4")) { - //用户选择标志归位 - MergeProgressDialog.FLAG_USER_HANDLE = false; - //处理弹窗 - MergeProgressDialog.existsSameFileDialog(dialog.getContext(), completeOutPath, cacheFile, ffmpegCallback); - //用户没有选择就休眠等待 - while (!MergeProgressDialog.FLAG_USER_HANDLE) { - Thread.sleep(600); - } - - } else { - - //构造ffmpeg命令 - cmdStr = String.format(MergeProgressDialog.cmdTemplate, cacheFile.getAudioPath(), cacheFile.getVideoPath(), completeOutPath); - cmd = cmdStr.split(" "); - - //LogUtils.e(cmdStr); - //执行命令 - RxFFmpegInvoke.getInstance() - .runCommand(cmd, ffmpegCallback); + //合并或复制视频 + switch (exportType) { + case 0: + completeOutPathSuf = completeOutPath + ".mp4"; + if (FileUtils.isFileExists(completeOutPathSuf)) { + //处理已经存在的文件弹窗 + MergeProgressDialog.handleExistsFileDialog(dialog.getContext(), completeOutPathSuf, cacheFile, ffmpegCallback, exportType, exportDanmaku); + } else { + cmdStr = String.format(MergeProgressDialog.CMD_TEMPLATE, cacheFile.getAudioPath(), cacheFile.getVideoPath(), completeOutPathSuf); + RxFfmpegTools.runCommand(cmdStr, ffmpegCallback); + //是否导出弹幕 + if (exportDanmaku) { + FileUtils.copy(cacheFile.getDanmakuPath(), completeOutPath + ".xml"); + } + } + break; + case 1: + completeOutPathSuf = completeOutPath + ".mp4"; + if (FileUtils.isFileExists(completeOutPathSuf)) { + MergeProgressDialog.handleExistsFileDialog(dialog.getContext(), completeOutPathSuf, cacheFile, ffmpegCallback, exportType, exportDanmaku); + } else { + MergeProgressDialog.copyVideoAudioWithProgress(cacheFile.getVideoPath(), completeOutPathSuf, ffmpegCallback); + //是否导出弹幕 + if (exportDanmaku) { + FileUtils.copy(cacheFile.getDanmakuPath(), completeOutPath + ".xml"); + } + } + break; + case 2: + completeOutPathSuf = completeOutPath + ".mp3"; + if (FileUtils.isFileExists(completeOutPathSuf)) { + MergeProgressDialog.handleExistsFileDialog(dialog.getContext(), completeOutPathSuf, cacheFile, ffmpegCallback, exportType, exportDanmaku); + } else { + MergeProgressDialog.copyVideoAudioWithProgress(cacheFile.getAudioPath(), completeOutPathSuf, ffmpegCallback); + //是否导出弹幕 + if (exportDanmaku) { + FileUtils.copy(cacheFile.getDanmakuPath(), completeOutPath + ".xml"); + } + } + break; + default: } } - } })) .setTaskChainCallback(new TaskChainCallbackAdapter() { @@ -135,42 +184,126 @@ public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITas }) .start(); + } + + /** + * 将uri转换为file并把路径返回存入CacheFile中 + * + * @param cacheFile + * @return 转换成功或不是Android11返回true + */ + public static CacheFile cacheFileUri2File(CacheFile cacheFile) { + if (cacheFile.getUseUri()) { + //uri转byte + byte[] bytesAudio = UriUtils.uri2Bytes(Uri.parse(cacheFile.getAudioPath())); + byte[] bytesVideo = UriUtils.uri2Bytes(Uri.parse(cacheFile.getVideoPath())); + byte[] bytesDanmaku = UriUtils.uri2Bytes(Uri.parse(cacheFile.getDanmakuPath())); + //获取临时文件名 + String audioTemp = ConfigData.TYPE_OUTPUT_FILE_PATH_TEMP + "/audio.mp3"; + String videoTemp = ConfigData.TYPE_OUTPUT_FILE_PATH_TEMP + "/video.mp4"; + String danmakuTemp = ConfigData.TYPE_OUTPUT_FILE_PATH_TEMP + "/danmaku.xml"; + //删除已经存在的临时文件名 + FileUtils.delete(audioTemp); + FileUtils.delete(videoTemp); + FileUtils.delete(danmakuTemp); + //byte转file + FileIOUtils.writeFileFromBytesByChannel(audioTemp, bytesAudio, true); + boolean success = FileIOUtils.writeFileFromBytesByChannel(videoTemp, bytesVideo, true); + FileIOUtils.writeFileFromBytesByChannel(danmakuTemp, bytesDanmaku, true); + //拷贝一份,不能影响源CacheFile + CacheFile tempCacheFile = null; + try { + tempCacheFile = cacheFile.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + + if (tempCacheFile != null) { + //重新设置路径 + tempCacheFile.setAudioPath(audioTemp); + tempCacheFile.setVideoPath(videoTemp); + tempCacheFile.setDanmakuPath(danmakuTemp); + } + + return tempCacheFile; + } else { + return cacheFile; + } } + /** + * 复制Video、Audio带有进度设置 + * + * @param src + * @param target + * @param ffmpegCallback + */ + public static void copyVideoAudioWithProgress(String src, String target, RxFFmpegCallback ffmpegCallback) { + boolean success = FileUtils.copy(src, target); + if (ffmpegCallback == null) { + return; + } + if (success) { + ffmpegCallback.onFinish(); + } else { + ffmpegCallback.onError(src + "复制失败"); + } + } + /** * 存在相同文件处理弹窗 * * @param context - * @param completeOutPath + * @param completeOutPathSuf * @param cacheFile * @param ffmpegCallback */ - public static void existsSameFileDialog(Context context, String completeOutPath, CacheFile cacheFile, RxFFmpegCallback ffmpegCallback) { + public static void handleExistsFileDialog(Context context, String completeOutPathSuf, CacheFile cacheFile, RxFFmpegCallback ffmpegCallback, int type, boolean exportDanmaku) throws InterruptedException { + //用户选择标志归位 + MergeProgressDialog.FLAG_USER_HANDLE = false; + //放在主线程中去执行 XTask.postToMain(new Runnable() { @Override public void run() { new MaterialDialog.Builder(context) .title("文件已经存在") - .content(completeOutPath + ".mp4已存在,请选择处理方法") + .content(completeOutPathSuf + "\n已存在,请选择处理方法") .cancelable(false) .positiveText("都保存") .onPositive(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + //获取扩展名 + String extension = FileTools.getFileExtension(completeOutPathSuf); + //设置新名称 int k = 0; - while (FileUtils.isFileExists(completeOutPath + "(" + k + ").mp4")) { + String reName; + do { + reName = completeOutPathSuf.replace("." + extension, "(" + k + ")." + extension); k++; + } while (FileUtils.isFileExists(reName)); + + //是否导出弹幕 + if (exportDanmaku) { + FileUtils.copy(cacheFile.getDanmakuPath(), reName.replace(extension, "xml")); } - //构造ffmpeg命令 - String cmdStr = String.format(MergeProgressDialog.cmdTemplate, cacheFile.getAudioPath(), cacheFile.getVideoPath(), completeOutPath + "(" + k + ")"); - String[] cmd = cmdStr.split(" "); - //LogUtils.e(cmdStr); - //执行命令 - RxFFmpegInvoke.getInstance() - .runCommand(cmd, ffmpegCallback); + switch (type) { + case 0: + //构造ffmpeg命令 + String cmdStr = String.format(MergeProgressDialog.CMD_TEMPLATE, cacheFile.getAudioPath(), cacheFile.getVideoPath(), reName); + RxFfmpegTools.runCommand(cmdStr, ffmpegCallback); + break; + case 1: + MergeProgressDialog.copyVideoAudioWithProgress(cacheFile.getVideoPath(), reName, ffmpegCallback); + break; + case 2: + MergeProgressDialog.copyVideoAudioWithProgress(cacheFile.getAudioPath(), reName, ffmpegCallback); + break; + default: + } dialog.dismiss(); } @@ -179,15 +312,34 @@ public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) .onNeutral(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + //获取扩展名 + String extension = FileTools.getFileExtension(completeOutPathSuf); //删除已存在的 - FileUtils.delete(completeOutPath + ".mp4"); - //构造ffmpeg命令 - String cmdStr = String.format(MergeProgressDialog.cmdTemplate, cacheFile.getAudioPath(), cacheFile.getVideoPath(), completeOutPath); - String[] cmd = cmdStr.split(" "); - //LogUtils.e(cmdStr); - //执行命令 - RxFFmpegInvoke.getInstance() - .runCommand(cmd, ffmpegCallback); + FileUtils.delete(completeOutPathSuf); + + switch (type) { + case 0: + //构造ffmpeg命令 + String cmdStr = String.format(MergeProgressDialog.CMD_TEMPLATE, cacheFile.getAudioPath(), cacheFile.getVideoPath(), completeOutPathSuf); + RxFfmpegTools.runCommand(cmdStr, ffmpegCallback); + break; + case 1: + MergeProgressDialog.copyVideoAudioWithProgress(cacheFile.getVideoPath(), completeOutPathSuf, ffmpegCallback); + break; + case 2: + MergeProgressDialog.copyVideoAudioWithProgress(cacheFile.getAudioPath(), completeOutPathSuf, ffmpegCallback); + break; + default: + } + + //是否导出弹幕 + if (exportDanmaku) { + String targetXml = completeOutPathSuf.replace(extension, "xml"); + //删除已存在的 + FileUtils.delete(targetXml); + FileUtils.copy(cacheFile.getDanmakuPath(), targetXml); + } + dialog.dismiss(); } }) @@ -209,6 +361,14 @@ public void onDismiss(DialogInterface dialog) { .show(); } }); + + + //用户没有选择就休眠等待 + while (!MergeProgressDialog.FLAG_USER_HANDLE) { + Thread.sleep(600); + } + + } } diff --git a/app/src/main/java/com/molihua/hlbmerge/entity/CacheFile.java b/app/src/main/java/com/molihua/hlbmerge/entity/CacheFile.java index 138c6d0..d9e3deb 100644 --- a/app/src/main/java/com/molihua/hlbmerge/entity/CacheFile.java +++ b/app/src/main/java/com/molihua/hlbmerge/entity/CacheFile.java @@ -1,5 +1,10 @@ package com.molihua.hlbmerge.entity; +import androidx.annotation.NonNull; +import androidx.documentfile.provider.DocumentFile; + +import java.io.Serializable; +import java.util.ArrayList; import java.util.List; /** @@ -8,7 +13,7 @@ * @Date: 2022/12/21/17:13 * @Description: */ -public class CacheFile { +public class CacheFile implements Serializable, Cloneable { //如果是合集则为0,如果是章节则是1 private Integer flag; //整体是否可见 @@ -37,6 +42,10 @@ public class CacheFile { private Boolean boxCheck; //存储blv格式的文件路径 private List blvPathList; + //是否使用uri地址 + private Boolean useUri; + //document + private DocumentFile documentFile; public CacheFile() { } @@ -158,6 +167,34 @@ public CacheFile setBlvPathList(List blvPathList) { return this; } + public Boolean getUseUri() { + return useUri; + } + + public CacheFile setUseUri(Boolean useUri) { + this.useUri = useUri; + return this; + } + + public DocumentFile getDocumentFile() { + return documentFile; + } + + public CacheFile setDocumentFile(DocumentFile documentFile) { + this.documentFile = documentFile; + return this; + } + + @NonNull + @Override + public CacheFile clone() throws CloneNotSupportedException { + CacheFile cloneCacheFile = (CacheFile) super.clone(); + if (blvPathList != null) { + cloneCacheFile.setBlvPathList(new ArrayList<>(blvPathList)); + } + return cloneCacheFile; + } + @Override public String toString() { return "CacheFile{" + @@ -173,6 +210,10 @@ public String toString() { ", danmakuPath='" + danmakuPath + '\'' + ", boxVisibility=" + boxVisibility + ", boxCheck=" + boxCheck + + ", useUri=" + useUri + + ", documentFile=" + documentFile + '}'; } + + } diff --git a/app/src/main/java/com/molihua/hlbmerge/fragment/AbstractMainFileShowFragment.java b/app/src/main/java/com/molihua/hlbmerge/fragment/AbstractMainFileShowFragment.java index 5c83461..6e5d0ac 100644 --- a/app/src/main/java/com/molihua/hlbmerge/fragment/AbstractMainFileShowFragment.java +++ b/app/src/main/java/com/molihua/hlbmerge/fragment/AbstractMainFileShowFragment.java @@ -9,4 +9,6 @@ * @Description: */ public abstract class AbstractMainFileShowFragment extends AbstractMainFragment implements IMainFileShowFragment { + + } diff --git a/app/src/main/java/com/molihua/hlbmerge/fragment/AbstractMainFragment.java b/app/src/main/java/com/molihua/hlbmerge/fragment/AbstractMainFragment.java index fe59738..fcea923 100644 --- a/app/src/main/java/com/molihua/hlbmerge/fragment/AbstractMainFragment.java +++ b/app/src/main/java/com/molihua/hlbmerge/fragment/AbstractMainFragment.java @@ -22,4 +22,6 @@ public void onAttach(@NonNull Context context) { abstractMainActivity = (AbstractMainActivity) mActivity; } } + + } diff --git a/app/src/main/java/com/molihua/hlbmerge/fragment/impl/MainFileShowFragment.java b/app/src/main/java/com/molihua/hlbmerge/fragment/impl/MainFileShowFragment.java index bf3b098..a30e5fd 100644 --- a/app/src/main/java/com/molihua/hlbmerge/fragment/impl/MainFileShowFragment.java +++ b/app/src/main/java/com/molihua/hlbmerge/fragment/impl/MainFileShowFragment.java @@ -1,5 +1,8 @@ package com.molihua.hlbmerge.fragment.impl; +import android.annotation.SuppressLint; +import android.content.Intent; +import android.net.Uri; import android.view.View; import androidx.annotation.NonNull; @@ -19,6 +22,10 @@ import com.molihua.hlbmerge.service.BaseCacheFileManager; import com.molihua.hlbmerge.service.ICacheFileManager; import com.molihua.hlbmerge.service.impl.PathCacheFileManager; +import com.molihua.hlbmerge.service.impl.UriCacheFileManager; +import com.molihuan.pathselector.utils.FileTools; +import com.molihuan.pathselector.utils.PermissionsTools; +import com.molihuan.pathselector.utils.VersionTool; import java.util.ArrayList; import java.util.List; @@ -39,6 +46,7 @@ public class MainFileShowFragment extends AbstractMainFileShowFragment implement private CacheFileListAdapter cacheFileListAdapter; private ICacheFileManager pathCacheFileManager; + private ICacheFileManager uriCacheFileManager; //当前是否为多选模式 private boolean multipleSelectionMode = false; @@ -56,7 +64,13 @@ public void getComponents(View view) { @Override public void initData() { + //权限申请 +// UriTool.grantedUriPermission(ConfigData.getCacheFilePath(), this); + + pathCacheFileManager = new PathCacheFileManager(); + uriCacheFileManager = new UriCacheFileManager(this); + allCacheFileList = new ArrayList<>(); updateCollectionFileList(); } @@ -102,8 +116,8 @@ public void onItemClick(@NonNull BaseQuickAdapter adapter, @NonNull View v refreshCacheFileList(); break; case BaseCacheFileManager.FLAG_CACHE_FILE_CHAPTER: - - MergeOptionDialog.showMergeOptionDialog(item, mActivity); + //打开合并弹窗 + MergeOptionDialog.showMergeOptionDialog(item, this); break; case BaseCacheFileManager.FLAG_CACHE_FILE_BACK: updateCollectionFileList(); @@ -131,23 +145,47 @@ public boolean onItemLongClick(@NonNull BaseQuickAdapter adapter, @NonNull return true; } - return false; } @Override public List updateCollectionFileList() { - return pathCacheFileManager.updateCollectionFileList(ConfigData.getCacheFilePath(), allCacheFileList); + //是否需要使用uri + boolean dataUseUri = FileTools.underAndroidDataUseUri(ConfigData.getCacheFilePath()); + + if (dataUseUri) { + allCacheFileList = uriCacheFileManager.updateCollectionFileList(ConfigData.getCacheFilePath(), allCacheFileList); + } else { + allCacheFileList = pathCacheFileManager.updateCollectionFileList(ConfigData.getCacheFilePath(), allCacheFileList); + } + + return allCacheFileList; } @Override public List updateChapterFileList() { - return pathCacheFileManager.updateChapterFileList(allCacheFileList.get(0).getCollectionPath(), allCacheFileList); + boolean dataUseUri = FileTools.underAndroidDataUseUri(ConfigData.getCacheFilePath()); + + if (dataUseUri) { + allCacheFileList = uriCacheFileManager.updateChapterFileList(allCacheFileList.get(0).getCollectionPath(), allCacheFileList); + } else { + allCacheFileList = pathCacheFileManager.updateChapterFileList(allCacheFileList.get(0).getCollectionPath(), allCacheFileList); + } + + return allCacheFileList; } @Override public List updateChapterFileList(String collectionPath) { - return pathCacheFileManager.updateChapterFileList(collectionPath, allCacheFileList); + boolean dataUseUri = FileTools.underAndroidDataUseUri(ConfigData.getCacheFilePath()); + + if (dataUseUri) { + allCacheFileList = uriCacheFileManager.updateChapterFileList(collectionPath, allCacheFileList); + } else { + allCacheFileList = pathCacheFileManager.updateChapterFileList(collectionPath, allCacheFileList); + } + + return allCacheFileList; } @Override @@ -228,14 +266,37 @@ public boolean onBackPressed() { return true; } - Integer flag = allCacheFileList.get(0).getFlag(); - if (flag == BaseCacheFileManager.FLAG_CACHE_FILE_BACK || flag == BaseCacheFileManager.FLAG_CACHE_FILE_CHAPTER) { - updateCollectionFileList(); - refreshCacheFileList(); - return true; + if (allCacheFileList != null && allCacheFileList.size() > 0) { + Integer flag = allCacheFileList.get(0).getFlag(); + if (flag != BaseCacheFileManager.FLAG_CACHE_FILE_COLLECTION) { + updateCollectionFileList(); + refreshCacheFileList(); + return true; + } } - return false; } + + + @SuppressLint("WrongConstant") + @Override + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + //保存这个uri目录的访问权限 + if (VersionTool.isAndroid11()) { + if (requestCode == PermissionsTools.PERMISSION_REQUEST_CODE) { + Uri uri; + if ((uri = data.getData()) != null) { + mActivity.getContentResolver() + .takePersistableUriPermission(uri, + data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + ); + } + //更新列表数据 + updateCollectionFileList(); + } + } + super.onActivityResult(requestCode, resultCode, data); + } + } diff --git a/app/src/main/java/com/molihua/hlbmerge/fragment/impl/MainHandleFragment.java b/app/src/main/java/com/molihua/hlbmerge/fragment/impl/MainHandleFragment.java index 3d1ada4..90c9128 100644 --- a/app/src/main/java/com/molihua/hlbmerge/fragment/impl/MainHandleFragment.java +++ b/app/src/main/java/com/molihua/hlbmerge/fragment/impl/MainHandleFragment.java @@ -64,7 +64,7 @@ public void onClick(View v) { } } else if (id == R.id.center_tv) { - MergeOptionDialog.showMergeOptionDialog(abstractMainActivity.getSelectedCacheFileList(), mActivity); + MergeOptionDialog.showMergeOptionDialog(abstractMainActivity.getSelectedCacheFileList(), abstractMainActivity.getMainFileShowFragment()); } else if (id == R.id.right_tv) { abstractMainActivity.openCloseMultipleMode(false); } diff --git a/app/src/main/java/com/molihua/hlbmerge/fragment/impl/MainTitlebarFragment.java b/app/src/main/java/com/molihua/hlbmerge/fragment/impl/MainTitlebarFragment.java index 931b986..df1796e 100644 --- a/app/src/main/java/com/molihua/hlbmerge/fragment/impl/MainTitlebarFragment.java +++ b/app/src/main/java/com/molihua/hlbmerge/fragment/impl/MainTitlebarFragment.java @@ -87,7 +87,13 @@ public void onClick(View v) { return; } - CacheFile cacheFile = abstractMainActivity.getAllCacheFileList().get(0); + List allCacheFileList = abstractMainActivity.getAllCacheFileList(); + + if (allCacheFileList.size() == 0) { + return; + } + + CacheFile cacheFile = allCacheFileList.get(0); if (cacheFile.getFlag() == BaseCacheFileManager.FLAG_CACHE_FILE_COLLECTION) { abstractMainActivity.updateCollectionFileList(); } else { @@ -151,7 +157,7 @@ public List filterCacheFileList(String key) { cacheFile.setWholeVisibility(View.VISIBLE); break; default: - + } } diff --git a/app/src/main/java/com/molihua/hlbmerge/interfaces/IMainFileShowFragment.java b/app/src/main/java/com/molihua/hlbmerge/interfaces/IMainFileShowFragment.java index 2e7f2b2..8861a2c 100644 --- a/app/src/main/java/com/molihua/hlbmerge/interfaces/IMainFileShowFragment.java +++ b/app/src/main/java/com/molihua/hlbmerge/interfaces/IMainFileShowFragment.java @@ -11,8 +11,19 @@ public interface IMainFileShowFragment { List updateCollectionFileList(); + /** + * 用来刷新的 + * + * @return + */ List updateChapterFileList(); + /** + * 进入合集中 + * + * @param collectionPath + * @return + */ List updateChapterFileList(String collectionPath); List getSelectedCacheFileList(); @@ -31,8 +42,17 @@ public interface IMainFileShowFragment { boolean isMultipleSelectionMode(); + /** + * 刷新UI + */ void refreshCacheFileList(); + /** + * 设置整体是否可见 + * + * @param state + * @return + */ List setWholeVisible(boolean state); } diff --git a/app/src/main/java/com/molihua/hlbmerge/service/BaseCacheFileManager.java b/app/src/main/java/com/molihua/hlbmerge/service/BaseCacheFileManager.java index d3b50af..3b6a61a 100644 --- a/app/src/main/java/com/molihua/hlbmerge/service/BaseCacheFileManager.java +++ b/app/src/main/java/com/molihua/hlbmerge/service/BaseCacheFileManager.java @@ -1,13 +1,14 @@ package com.molihua.hlbmerge.service; +import android.annotation.SuppressLint; import android.view.View; +import com.molihua.hlbmerge.adapter.CacheFileListAdapter; import com.molihua.hlbmerge.entity.CacheFile; -import com.molihua.hlbmerge.utils.FileTools; -import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * @ClassName: BaseCacheFileManager @@ -33,54 +34,99 @@ public List initCollectionFileList(String path, List cache return cacheFileList; } - /** - * 将合集item转换为章节item - * - * @param collectionCacheFileList - * @return - */ - public static List collection2ChapterCacheFileList(List collectionCacheFileList) { - //如果已经是章节了就直接返回 - if (collectionCacheFileList.get(0).getFlag() == BaseCacheFileManager.FLAG_CACHE_FILE_CHAPTER) { - return collectionCacheFileList; + @Override + public List setBoxVisible(List cacheFileList, CacheFileListAdapter cacheFileAdapter, boolean state) { + + int type; + if (state) { + type = View.VISIBLE; + } else { + type = View.INVISIBLE; + } + + CacheFile cacheFile; + for (int i = 0; i < cacheFileList.size(); i++) { + cacheFile = cacheFileList.get(i); + //判断是不是返回item + if (cacheFile.getFlag() == BaseCacheFileManager.FLAG_CACHE_FILE_BACK) { + continue; + } + cacheFile.setBoxVisibility(type); + //显示和不显示时都设为不选中防止有缓存 + cacheFile.setBoxCheck(false); } - List tempList = new ArrayList<>(); - - String[] needPath = new String[4]; - String[] names = new String[2]; - - String collectionPath; - //遍历所有合集 - for (int n = 0; n < collectionCacheFileList.size(); n++) { - //获取一个合集路径 - collectionPath = collectionCacheFileList.get(n).getCollectionPath(); - //获取一个合集下面所有的章节 - File[] chapterFile = FileTools.getCollectionChapterFile(collectionPath); - for (int i = 0; i < chapterFile.length; i++) { - //获取章节里需要的路径 - needPath = FileTools.getNeedPath(chapterFile[i], needPath); - //获取合集名称和章节名称 - names = FileTools.getCollectionChapterName(needPath[2], names); - tempList.add( - new CacheFile() - .setFlag(BaseCacheFileManager.FLAG_CACHE_FILE_CHAPTER) - .setWholeVisibility(View.VISIBLE) - .setCollectionPath(collectionPath) - .setCollectionName(names[0]) - .setChapterPath(chapterFile[i].getAbsolutePath()) - .setChapterName(names[1]) - .setAudioPath(needPath[0]) - .setVideoPath(needPath[1]) - .setJsonPath(needPath[2]) - .setDanmakuPath(needPath[3]) - .setBoxVisibility(View.INVISIBLE) - .setBoxCheck(false) - ); + return cacheFileList; + } + + @Override + public List setBoxChecked(List cacheFileList, CacheFileListAdapter cacheFileAdapter, boolean state) { + CacheFile cacheFile; + for (int i = 0; i < cacheFileList.size(); i++) { + cacheFile = cacheFileList.get(i); + //判断是不是返回item + if (cacheFile.getFlag() == BaseCacheFileManager.FLAG_CACHE_FILE_BACK) { + continue; + } + //如果是不可见的 + if (cacheFile.getBoxVisibility() != View.VISIBLE || cacheFile.getWholeVisibility() != View.VISIBLE) { + cacheFile.setBoxCheck(false); + } else { + cacheFile.setBoxCheck(state); } } + return cacheFileList; + } + + @Override + public List setWholeVisible(List cacheFileList, boolean state) { + int type; + if (state) { + type = View.VISIBLE; + } else { + type = View.GONE; + } - return tempList; + CacheFile cacheFile; + for (int i = 0; i < cacheFileList.size(); i++) { + cacheFile = cacheFileList.get(i); + //判断是不是返回item + if (cacheFile.getFlag() == BaseCacheFileManager.FLAG_CACHE_FILE_BACK) { + continue; + } + cacheFile.setWholeVisibility(type); + //显示和不显示时都设为不选中防止有缓存 + cacheFile.setBoxCheck(false); + } + + return cacheFileList; } + + @Override + public List getSelectedCacheFileList(List allCacheFileList, List selectedCacheFileList) { + Objects.requireNonNull(allCacheFileList, "allCacheFileList is null"); + //初始化 + if (selectedCacheFileList == null) { + selectedCacheFileList = new ArrayList<>(); + } else { + selectedCacheFileList.clear(); + } + //把勾选的添加到选择列表中 + for (CacheFile cacheFile : allCacheFileList) { + if (cacheFile.getBoxCheck()) { + selectedCacheFileList.add(cacheFile); + } + } + + return selectedCacheFileList; + } + + @SuppressLint("NotifyDataSetChanged") + @Override + public void refreshCacheFileList(CacheFileListAdapter cacheFileAdapter) { + cacheFileAdapter.notifyDataSetChanged(); + } + + } diff --git a/app/src/main/java/com/molihua/hlbmerge/service/impl/PathCacheFileManager.java b/app/src/main/java/com/molihua/hlbmerge/service/impl/PathCacheFileManager.java index 30f2006..582ef69 100644 --- a/app/src/main/java/com/molihua/hlbmerge/service/impl/PathCacheFileManager.java +++ b/app/src/main/java/com/molihua/hlbmerge/service/impl/PathCacheFileManager.java @@ -1,9 +1,7 @@ package com.molihua.hlbmerge.service.impl; -import android.annotation.SuppressLint; import android.view.View; -import com.molihua.hlbmerge.adapter.CacheFileListAdapter; import com.molihua.hlbmerge.entity.CacheFile; import com.molihua.hlbmerge.service.BaseCacheFileManager; import com.molihua.hlbmerge.utils.FileTools; @@ -31,6 +29,9 @@ public List updateCollectionFileList(String path, List cac String[] names = new String[2]; //获取所有的合集 File[] collectionFile = FileTools.getCollectionChapterFile(path); + if (collectionFile == null) { + return cacheFileList; + } for (int i = 0; i < collectionFile.length; i++) { //获取每一个集合中的第一个章节 File oneChapterPath = Objects.requireNonNull(collectionFile[i].listFiles())[0]; @@ -51,6 +52,7 @@ public List updateCollectionFileList(String path, List cac .setDanmakuPath(needPath[3]) .setBoxVisibility(View.INVISIBLE) .setBoxCheck(false) + .setUseUri(false) ); } @@ -67,12 +69,13 @@ public List initChapterFileList(String collectionPath, List updateChapterFileList(String collectionPath, List setBoxVisible(List cacheFileList, CacheFileListAdapter cacheFileAdapter, boolean state) { - - int type; - if (state) { - type = View.VISIBLE; - } else { - type = View.INVISIBLE; + /** + * 将合集item转换为章节item + * + * @param collectionCacheFileList + * @return + */ + public static List collection2ChapterCacheFileList(List collectionCacheFileList) { + Objects.requireNonNull(collectionCacheFileList, "collectionCacheFileList is null"); + + //如果已经是章节了就直接返回 + if (collectionCacheFileList.get(0).getFlag() == BaseCacheFileManager.FLAG_CACHE_FILE_CHAPTER) { + return collectionCacheFileList; } - CacheFile cacheFile; - for (int i = 0; i < cacheFileList.size(); i++) { - cacheFile = cacheFileList.get(i); - //判断是不是返回item - if (cacheFile.getFlag() == BaseCacheFileManager.FLAG_CACHE_FILE_BACK) { - continue; - } - cacheFile.setBoxVisibility(type); - //显示和不显示时都设为不选中防止有缓存 - cacheFile.setBoxCheck(false); - } + List tempList = new ArrayList<>(); - return cacheFileList; - } + String[] needPath = new String[4]; + String[] names = new String[2]; - @Override - public List setBoxChecked(List cacheFileList, CacheFileListAdapter cacheFileAdapter, boolean state) { - CacheFile cacheFile; - for (int i = 0; i < cacheFileList.size(); i++) { - cacheFile = cacheFileList.get(i); - //判断是不是返回item - if (cacheFile.getFlag() == BaseCacheFileManager.FLAG_CACHE_FILE_BACK) { - continue; - } - //如果是不可见的 - if (cacheFile.getBoxVisibility() != View.VISIBLE || cacheFile.getWholeVisibility() != View.VISIBLE) { - cacheFile.setBoxCheck(false); - } else { - cacheFile.setBoxCheck(state); + String collectionPath; + //遍历所有合集 + for (int n = 0; n < collectionCacheFileList.size(); n++) { + //获取一个合集路径 + collectionPath = collectionCacheFileList.get(n).getCollectionPath(); + //获取一个合集下面所有的章节 + File[] chapterFile = FileTools.getCollectionChapterFile(collectionPath); + for (int i = 0; i < chapterFile.length; i++) { + //获取章节里需要的路径 + needPath = FileTools.getNeedPath(chapterFile[i], needPath); + //获取合集名称和章节名称 + names = FileTools.getCollectionChapterName(needPath[2], names); + tempList.add( + new CacheFile() + .setFlag(BaseCacheFileManager.FLAG_CACHE_FILE_CHAPTER) + .setWholeVisibility(View.VISIBLE) + .setCollectionPath(collectionPath) + .setCollectionName(names[0]) + .setChapterPath(chapterFile[i].getAbsolutePath()) + .setChapterName(names[1]) + .setAudioPath(needPath[0]) + .setVideoPath(needPath[1]) + .setJsonPath(needPath[2]) + .setDanmakuPath(needPath[3]) + .setBoxVisibility(View.INVISIBLE) + .setBoxCheck(false) + .setUseUri(false) + ); } } - return cacheFileList; - } - - @Override - public List setWholeVisible(List cacheFileList, boolean state) { - int type; - if (state) { - type = View.VISIBLE; - } else { - type = View.GONE; - } - CacheFile cacheFile; - for (int i = 0; i < cacheFileList.size(); i++) { - cacheFile = cacheFileList.get(i); - //判断是不是返回item - if (cacheFile.getFlag() == BaseCacheFileManager.FLAG_CACHE_FILE_BACK) { - continue; - } - cacheFile.setWholeVisibility(type); - //显示和不显示时都设为不选中防止有缓存 - cacheFile.setBoxCheck(false); - } - - return cacheFileList; + return tempList; } - @Override - public List getSelectedCacheFileList(List allCacheFileList, List selectedCacheFileList) { - Objects.requireNonNull(allCacheFileList, "allCacheFileList is null"); - //初始化 - if (selectedCacheFileList == null) { - selectedCacheFileList = new ArrayList<>(); - } else { - selectedCacheFileList.clear(); - } - //把勾选的添加到选择列表中 - for (CacheFile cacheFile : allCacheFileList) { - if (cacheFile.getBoxCheck()) { - selectedCacheFileList.add(cacheFile); - } - } - - return selectedCacheFileList; - } - @SuppressLint("NotifyDataSetChanged") - @Override - public void refreshCacheFileList(CacheFileListAdapter cacheFileAdapter) { - cacheFileAdapter.notifyDataSetChanged(); - } } diff --git a/app/src/main/java/com/molihua/hlbmerge/service/impl/UriCacheFileManager.java b/app/src/main/java/com/molihua/hlbmerge/service/impl/UriCacheFileManager.java index 73ffaa0..78a9665 100644 --- a/app/src/main/java/com/molihua/hlbmerge/service/impl/UriCacheFileManager.java +++ b/app/src/main/java/com/molihua/hlbmerge/service/impl/UriCacheFileManager.java @@ -1,12 +1,14 @@ package com.molihua.hlbmerge.service.impl; -import android.annotation.SuppressLint; +import android.net.Uri; import android.view.View; -import com.molihua.hlbmerge.adapter.CacheFileListAdapter; +import androidx.documentfile.provider.DocumentFile; +import androidx.fragment.app.Fragment; + import com.molihua.hlbmerge.entity.CacheFile; import com.molihua.hlbmerge.service.BaseCacheFileManager; -import com.molihua.hlbmerge.utils.FileTools; +import com.molihua.hlbmerge.utils.UriTool; import java.io.File; import java.util.ArrayList; @@ -21,36 +23,49 @@ */ public class UriCacheFileManager extends BaseCacheFileManager { + private Fragment fragment; + + public UriCacheFileManager(Fragment fragment) { + this.fragment = fragment; + } @Override public List updateCollectionFileList(String path, List cacheFileList) { //初始化列表 cacheFileList = initCollectionFileList(path, cacheFileList); - String[] needPath = new String[4]; + Uri[] needUri = new Uri[4]; String[] names = new String[2]; - //获取所有的合集 - File[] collectionFile = FileTools.getCollectionChapterFile(path); - for (int i = 0; i < collectionFile.length; i++) { - //获取每一个集合中的第一个章节 - File oneChapterPath = Objects.requireNonNull(collectionFile[i].listFiles())[0]; - //获取章节里需要的路径 - needPath = FileTools.getNeedPath(oneChapterPath, needPath); + //获取所有的合集路径 + DocumentFile[] collectionFiles = UriTool.getCollectionChapterFile(fragment, path); + + if (collectionFiles == null) { + return cacheFileList; + } + for (int i = 0; i < collectionFiles.length; i++) { + //获取每一个集合中的第一个章节路径 + DocumentFile oneChapterPath = Objects.requireNonNull(collectionFiles[i].listFiles())[0]; + + //获取章节里需要的Uri + needUri = UriTool.getNeedUri(oneChapterPath, needUri); + //获取合集名称和章节名称 - names = FileTools.getCollectionChapterName(needPath[2], names); + names = UriTool.getCollectionChapterName(needUri[2], names); + cacheFileList.add( new CacheFile() .setFlag(BaseCacheFileManager.FLAG_CACHE_FILE_COLLECTION) .setWholeVisibility(View.VISIBLE) - .setCollectionPath(collectionFile[i].getAbsolutePath()) + .setCollectionPath(path + File.separator + collectionFiles[i].getName()) .setCollectionName(names[0]) .setChapterName(names[1]) - .setAudioPath(needPath[0]) - .setVideoPath(needPath[1]) - .setJsonPath(needPath[2]) - .setDanmakuPath(needPath[3]) + .setAudioPath(needUri[0].toString()) + .setVideoPath(needUri[1].toString()) + .setJsonPath(needUri[2].toString()) + .setDanmakuPath(needUri[3].toString()) .setBoxVisibility(View.INVISIBLE) .setBoxCheck(false) + .setUseUri(true) ); } @@ -67,12 +82,13 @@ public List initChapterFileList(String collectionPath, List initChapterFileList(String collectionPath, List updateChapterFileList(String collectionPath, List cacheFileList) { cacheFileList = initChapterFileList(collectionPath, cacheFileList); - String[] needPath = new String[4]; + Uri[] needUri = new Uri[4]; String[] names = new String[2]; //获取一个合集下面所有的章节 - File[] chapterFile = FileTools.getCollectionChapterFile(collectionPath); + DocumentFile[] chapterFile = UriTool.getCollectionChapterFile(fragment, collectionPath); + + if (chapterFile == null) { + return cacheFileList; + } + for (int i = 0; i < chapterFile.length; i++) { - //获取章节里需要的路径 - needPath = FileTools.getNeedPath(chapterFile[i], needPath); + + //获取章节里需要的Uri + needUri = UriTool.getNeedUri(chapterFile[i], needUri); + //获取合集名称和章节名称 - names = FileTools.getCollectionChapterName(needPath[2], names); + names = UriTool.getCollectionChapterName(needUri[2], names); + cacheFileList.add( new CacheFile() .setFlag(BaseCacheFileManager.FLAG_CACHE_FILE_CHAPTER) .setWholeVisibility(View.VISIBLE) .setCollectionPath(collectionPath) .setCollectionName(names[0]) - .setChapterPath(chapterFile[i].getAbsolutePath()) + .setChapterPath(collectionPath + File.separator + chapterFile[i].getName()) .setChapterName(names[1]) - .setAudioPath(needPath[0]) - .setVideoPath(needPath[1]) - .setJsonPath(needPath[2]) - .setDanmakuPath(needPath[3]) + .setAudioPath(needUri[0].toString()) + .setVideoPath(needUri[1].toString()) + .setJsonPath(needUri[2].toString()) + .setDanmakuPath(needUri[3].toString()) .setBoxVisibility(View.INVISIBLE) .setBoxCheck(false) + .setUseUri(true) ); } return cacheFileList; } - @Override - public List setBoxVisible(List cacheFileList, CacheFileListAdapter cacheFileAdapter, boolean state) { - - int type; - if (state) { - type = View.VISIBLE; - } else { - type = View.INVISIBLE; + /** + * 将合集item转换为章节item + * + * @param collectionCacheFileList + * @return + */ + public static List collection2ChapterCacheFileList(Fragment fragment, List collectionCacheFileList) { + Objects.requireNonNull(collectionCacheFileList, "collectionCacheFileList is null"); + + //如果已经是章节了就直接返回 + if (collectionCacheFileList.get(0).getFlag() == BaseCacheFileManager.FLAG_CACHE_FILE_CHAPTER) { + return collectionCacheFileList; } - CacheFile cacheFile; - for (int i = 0; i < cacheFileList.size(); i++) { - cacheFile = cacheFileList.get(i); - //判断是不是返回item - if (cacheFile.getFlag() == BaseCacheFileManager.FLAG_CACHE_FILE_BACK) { - continue; - } - cacheFile.setBoxVisibility(type); - //显示和不显示时都设为不选中防止有缓存 - cacheFile.setBoxCheck(false); - } - - return cacheFileList; - } + List tempList = new ArrayList<>(); - @Override - public List setBoxChecked(List cacheFileList, CacheFileListAdapter cacheFileAdapter, boolean state) { - CacheFile cacheFile; - for (int i = 0; i < cacheFileList.size(); i++) { - cacheFile = cacheFileList.get(i); - //判断是不是返回item - if (cacheFile.getFlag() == BaseCacheFileManager.FLAG_CACHE_FILE_BACK) { - continue; - } - //如果是不可见的 - if (cacheFile.getBoxVisibility() != View.VISIBLE || cacheFile.getWholeVisibility() != View.VISIBLE) { - cacheFile.setBoxCheck(false); - } else { - cacheFile.setBoxCheck(state); - } - - } - return cacheFileList; - } - - @Override - public List setWholeVisible(List cacheFileList, boolean state) { - int type; - if (state) { - type = View.VISIBLE; - } else { - type = View.GONE; - } + Uri[] needUri = new Uri[4]; + String[] names = new String[2]; - CacheFile cacheFile; - for (int i = 0; i < cacheFileList.size(); i++) { - cacheFile = cacheFileList.get(i); - //判断是不是返回item - if (cacheFile.getFlag() == BaseCacheFileManager.FLAG_CACHE_FILE_BACK) { - continue; + String collectionPath; + //遍历所有合集 + for (int n = 0; n < collectionCacheFileList.size(); n++) { + //获取一个合集路径 + collectionPath = collectionCacheFileList.get(n).getCollectionPath(); + //获取一个合集下面所有的章节 + DocumentFile[] chapterFile = UriTool.getCollectionChapterFile(fragment, collectionPath); + for (int i = 0; i < chapterFile.length; i++) { + + //获取章节里需要的Uri + needUri = UriTool.getNeedUri(chapterFile[i], needUri); + + //获取合集名称和章节名称 + names = UriTool.getCollectionChapterName(needUri[2], names); + + tempList.add( + new CacheFile() + .setFlag(BaseCacheFileManager.FLAG_CACHE_FILE_CHAPTER) + .setWholeVisibility(View.VISIBLE) + .setCollectionPath(collectionPath) + .setCollectionName(names[0]) + .setChapterPath(collectionPath + File.separator + chapterFile[i].getName()) + .setChapterName(names[1]) + .setAudioPath(needUri[0].toString()) + .setVideoPath(needUri[1].toString()) + .setJsonPath(needUri[2].toString()) + .setDanmakuPath(needUri[3].toString()) + .setBoxVisibility(View.INVISIBLE) + .setBoxCheck(false) + .setUseUri(true) + ); } - cacheFile.setWholeVisibility(type); - //显示和不显示时都设为不选中防止有缓存 - cacheFile.setBoxCheck(false); - } - - return cacheFileList; - } - @Override - public List getSelectedCacheFileList(List allCacheFileList, List selectedCacheFileList) { - Objects.requireNonNull(allCacheFileList, "allCacheFileList is null"); - //初始化 - if (selectedCacheFileList == null) { - selectedCacheFileList = new ArrayList<>(); - } else { - selectedCacheFileList.clear(); - } - //把勾选的添加到选择列表中 - for (CacheFile cacheFile : allCacheFileList) { - if (cacheFile.getBoxCheck()) { - selectedCacheFileList.add(cacheFile); - } } - return selectedCacheFileList; + return tempList; } - @SuppressLint("NotifyDataSetChanged") - @Override - public void refreshCacheFileList(CacheFileListAdapter cacheFileAdapter) { - cacheFileAdapter.notifyDataSetChanged(); - } } diff --git a/app/src/main/java/com/molihua/hlbmerge/utils/FileTools.java b/app/src/main/java/com/molihua/hlbmerge/utils/FileTools.java index 51eb469..a1f1b67 100644 --- a/app/src/main/java/com/molihua/hlbmerge/utils/FileTools.java +++ b/app/src/main/java/com/molihua/hlbmerge/utils/FileTools.java @@ -1,6 +1,8 @@ package com.molihua.hlbmerge.utils; +import com.blankj.molihuan.utilcode.util.ConvertUtils; import com.blankj.molihuan.utilcode.util.FileIOUtils; +import com.blankj.molihuan.utilcode.util.StringUtils; import org.json.JSONException; import org.json.JSONObject; @@ -34,11 +36,16 @@ public static File[] getCollectionChapterFile(String path) { return file.listFiles(); } + public static String[] getCollectionChapterName(byte[] jsonByte, String[] result) { + //把jsonByte转换成json字符串 + String jsonStr = ConvertUtils.bytes2String(jsonByte); + return getCollectionChapterNameByJsonStr(jsonStr, result); + } /** * 获取合集和章节名称 * - * @param jsonPath + * @param jsonPath json文件路径 * @param result * @return result[0]合集名称 * result[1]章节名称 @@ -46,6 +53,22 @@ public static File[] getCollectionChapterFile(String path) { public static String[] getCollectionChapterName(String jsonPath, String[] result) { //把json文件转换成json字符串 String jsonStr = FileIOUtils.readFile2String(jsonPath, "UTF-8"); + return getCollectionChapterNameByJsonStr(jsonStr, result); + } + + /** + * 通过json字符串解析名称 + * + * @param jsonStr json字符串 + * @param result + * @return + */ + private static String[] getCollectionChapterNameByJsonStr(String jsonStr, String[] result) { + + if (StringUtils.isTrimEmpty(jsonStr)) { + return null; + } + JSONObject jsonObject; //将json字符串转换成json对象 try { diff --git a/app/src/main/java/com/molihua/hlbmerge/utils/RxFfmpegTools.java b/app/src/main/java/com/molihua/hlbmerge/utils/RxFfmpegTools.java index 344e0f9..8debc58 100644 --- a/app/src/main/java/com/molihua/hlbmerge/utils/RxFfmpegTools.java +++ b/app/src/main/java/com/molihua/hlbmerge/utils/RxFfmpegTools.java @@ -1,5 +1,9 @@ package com.molihua.hlbmerge.utils; +import com.molihua.hlbmerge.service.impl.RxFFmpegCallback; + +import io.microshow.rxffmpeg.RxFFmpegInvoke; + /** * @ClassName: RxFfmpegTools * @Author: molihuan @@ -7,6 +11,15 @@ * @Description: */ public class RxFfmpegTools { - + + + public static int runCommand(String cmdStr, RxFFmpegCallback ffmpegCallback) { + String[] cmd = cmdStr.split(" "); + + //LogUtils.e(cmdStr); + //执行命令 + return RxFFmpegInvoke.getInstance() + .runCommand(cmd, ffmpegCallback); + } } diff --git a/app/src/main/java/com/molihua/hlbmerge/utils/UriTool.java b/app/src/main/java/com/molihua/hlbmerge/utils/UriTool.java new file mode 100644 index 0000000..e5b16b5 --- /dev/null +++ b/app/src/main/java/com/molihua/hlbmerge/utils/UriTool.java @@ -0,0 +1,151 @@ +package com.molihua.hlbmerge.utils; + +import android.content.Context; +import android.net.Uri; + +import androidx.annotation.NonNull; +import androidx.documentfile.provider.DocumentFile; +import androidx.fragment.app.Fragment; + +import com.blankj.molihuan.utilcode.util.UriUtils; +import com.molihuan.pathselector.utils.PermissionsTools; +import com.molihuan.pathselector.utils.UriTools; +import com.xuexiang.xtask.XTask; +import com.xuexiang.xui.widget.dialog.materialdialog.DialogAction; +import com.xuexiang.xui.widget.dialog.materialdialog.MaterialDialog; + +import java.util.Objects; + +/** + * @ClassName: UriTool + * @Author: molihuan + * @Date: 2022/12/24/21:28 + * @Description: + */ +public class UriTool { + + public static void grantedUriPermission(String path, Fragment fragment) { + //获取上下文 + Context context = fragment.getContext(); + Objects.requireNonNull(context, "context is null"); + + Uri uri = UriTools.path2Uri(path, false); + //获取权限,没有权限返回null有权限返回授权uri字符串 + String existsPermission = PermissionsTools.existsGrantedUriPermission(uri, fragment); + + if (existsPermission == null) { + //没有权限申请权限 + new MaterialDialog.Builder(context) + .title("授权提示") + .content("需要授予\n" + path + "\n目录访问权限") + .cancelable(false) + .positiveText("授权") + .onPositive(new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + //申请权限 + PermissionsTools.goApplyUriPermissionPage(uri, fragment); + dialog.dismiss(); + } + }) + .negativeText("取消") + .onNegative(new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + dialog.dismiss(); + } + }) + .show(); + } + } + + public static DocumentFile[] getCollectionChapterFile(Fragment fragment, String currentPath) { + //获取上下文 + Context context = fragment.getContext(); + Objects.requireNonNull(context, "context is null"); + + Uri uri = UriTools.path2Uri(currentPath, false); + //获取权限,没有权限返回null有权限返回授权uri字符串 + String existsPermission = PermissionsTools.existsGrantedUriPermission(uri, fragment); + + if (existsPermission == null) { + //没有权限申请权限 + XTask.postToMain(new Runnable() { + @Override + public void run() { + //申请权限弹窗 + new MaterialDialog.Builder(context) + .title("授权提示") + .content("需要授予\n" + currentPath + "\n目录访问权限") + .cancelable(false) + .positiveText("授权") + .onPositive(new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + //申请权限 + PermissionsTools.goApplyUriPermissionPage(uri, fragment); + dialog.dismiss(); + } + }) + .negativeText("取消") + .onNegative(new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + dialog.dismiss(); + } + }) + .show(); + } + }); + + return null; + } + + Uri targetUri = Uri.parse(existsPermission + uri.toString().replaceFirst(UriTools.URI_PERMISSION_REQUEST_COMPLETE_PREFIX, "")); + DocumentFile rootDocumentFile = DocumentFile.fromSingleUri(context, targetUri); + Objects.requireNonNull(rootDocumentFile, "rootDocumentFile is null"); + //创建一个 DocumentFile表示以给定的 Uri根的文档树。其实就是获取子目录的权限 + DocumentFile pickedDir = rootDocumentFile.fromTreeUri(context, targetUri); + + Objects.requireNonNull(pickedDir, "pickedDir is null"); + DocumentFile[] documentFiles = pickedDir.listFiles(); + + return documentFiles; + } + + public static Uri[] getNeedUri(DocumentFile chapterFile, Uri[] result) { + DocumentFile[] files = chapterFile.listFiles(); + if (files != null) { + for (int i = 0; i < files.length; i++) { + if (files[i].isDirectory()) { + getNeedUri(files[i], result); + } else { + switch (files[i].getName()) { + case "audio.m4s": + result[0] = files[i].getUri(); + break; + case "video.m4s": + result[1] = files[i].getUri(); + break; + case "entry.json": + result[2] = files[i].getUri(); + break; + case "danmaku.xml": + result[3] = files[i].getUri(); + break; + } + } + } + } + return result; + } + + + public static String[] getCollectionChapterName(Uri jsonUri, String[] result) { + //uri转byte + byte[] jsonByte = UriUtils.uri2Bytes(jsonUri); + //通过jsonByte获取名称 + result = FileTools.getCollectionChapterName(jsonByte, result); + return result; + } +} diff --git a/app/src/main/java/com/molihua/hlbmerge/utils/UriTools.java b/app/src/main/java/com/molihua/hlbmerge/utils/UriTools.java deleted file mode 100644 index 113b89f..0000000 --- a/app/src/main/java/com/molihua/hlbmerge/utils/UriTools.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.molihua.hlbmerge.utils; - -/** - * @ClassName: UriTools - * @Author: molihuan - * @Date: 2022/12/24/21:28 - * @Description: - */ -public class UriTools { -}