完善导入、导出身份功能,目前可进入本地文件目录系统,任选目录导入或导出
No files matched your search
@@ -78,6 +78,7 @@ dependencies {
|
|||||||
implementation files('libs/gmhelper.jar')
|
implementation files('libs/gmhelper.jar')
|
||||||
implementation files('libs/BaiduLBS_Android.jar')
|
implementation files('libs/BaiduLBS_Android.jar')
|
||||||
implementation files('libs/min-dev-java-1.0-release.jar')
|
implementation files('libs/min-dev-java-1.0-release.jar')
|
||||||
|
implementation project(path: ':fileselectorlib')
|
||||||
// implementation files('libs/vpnjce-1.0.jar')
|
// implementation files('libs/vpnjce-1.0.jar')
|
||||||
|
|
||||||
// 在依赖中添加对jre的依赖,以尝试解决android.jar中原生javax与java中的javax的冲突问题
|
// 在依赖中添加对jre的依赖,以尝试解决android.jar中原生javax与java中的javax的冲突问题
|
||||||
|
|||||||
@@ -7,9 +7,14 @@ package com.pkusz.min_vpn_client.activity.login;
|
|||||||
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
@@ -30,6 +35,7 @@ import com.pkusz.min_vpn_client.utils.DigestUtilKt;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import br.com.simplepass.loadingbutton.customViews.CircularProgressButton;
|
import br.com.simplepass.loadingbutton.customViews.CircularProgressButton;
|
||||||
@@ -43,7 +49,11 @@ import cn.qjm253.quick_android_easy_bar.EasyBar;
|
|||||||
import cn.qjm253.quick_android_easy_bar.EasyBarExtensionsKt;
|
import cn.qjm253.quick_android_easy_bar.EasyBarExtensionsKt;
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import kotlin.jvm.functions.Function1;
|
import kotlin.jvm.functions.Function1;
|
||||||
import com.pkusz.min_vpn_client.utils.IdentityDump;
|
|
||||||
|
import com.pkusz.min_vpn_client.utils.identity.IdentityDump;
|
||||||
|
import com.pkusz.min_vpn_client.utils.identity.ScreenSizeUtils;
|
||||||
|
import com.zlylib.fileselectorlib.FileSelector;
|
||||||
|
import com.zlylib.fileselectorlib.utils.Const;
|
||||||
|
|
||||||
|
|
||||||
public class LoginActivity extends MINVpnBaseActivity<LoginActivityPresenter>
|
public class LoginActivity extends MINVpnBaseActivity<LoginActivityPresenter>
|
||||||
@@ -153,7 +163,12 @@ public class LoginActivity extends MINVpnBaseActivity<LoginActivityPresenter>
|
|||||||
ContextExtensionKt.hideSoftKeyboard(this,export_identity);
|
ContextExtensionKt.hideSoftKeyboard(this,export_identity);
|
||||||
export_identity.startAnimation();
|
export_identity.startAnimation();
|
||||||
export_identity.revertAnimation();
|
export_identity.revertAnimation();
|
||||||
new IdentityDump().exportIdentity();
|
//new IdentityDump().exportIdentity();
|
||||||
|
//showCustomizeDialog();
|
||||||
|
FileSelector.from(this)
|
||||||
|
.onlySelectFolder() //只能选择文件夹
|
||||||
|
.requestCode(2) //设置返回码
|
||||||
|
.start();
|
||||||
Toast.makeText(this, "You clicked export identity button", Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, "You clicked export identity button", Toast.LENGTH_SHORT).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -163,7 +178,19 @@ public class LoginActivity extends MINVpnBaseActivity<LoginActivityPresenter>
|
|||||||
ContextExtensionKt.hideSoftKeyboard(this,import_identity);
|
ContextExtensionKt.hideSoftKeyboard(this,import_identity);
|
||||||
import_identity.startAnimation();
|
import_identity.startAnimation();
|
||||||
import_identity.revertAnimation();
|
import_identity.revertAnimation();
|
||||||
new IdentityDump().importIdentity();
|
FileSelector.from(this)
|
||||||
|
// .onlyShowFolder() //只显示文件夹
|
||||||
|
//.onlySelectFolder() //只能选择文件夹
|
||||||
|
// .isSingle() // 只能选择一个
|
||||||
|
.setMaxCount(1) //设置最大选择数
|
||||||
|
.setFileTypes("png","jpg", "doc","apk", "mp3", "gif", "txt", "mp4", "zip","db","export") //设置文件类型
|
||||||
|
.setSortType(FileSelector.BY_NAME_ASC) //设置名字排序
|
||||||
|
//.setSortType(FileSelector.BY_TIME_ASC) //设置时间排序
|
||||||
|
//.setSortType(FileSelector.BY_SIZE_DESC) //设置大小排序
|
||||||
|
//.setSortType(FileSelector.BY_EXTENSION_DESC) //设置类型排序
|
||||||
|
.requestCode(1) //设置返回码
|
||||||
|
.setTargetPath("/storage/emulated/0/")
|
||||||
|
.start();
|
||||||
Toast.makeText(this, "You clicked import identity button", Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, "You clicked import identity button", Toast.LENGTH_SHORT).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -290,4 +317,73 @@ public class LoginActivity extends MINVpnBaseActivity<LoginActivityPresenter>
|
|||||||
public void syncVpnInfoFailed() {
|
public void syncVpnInfoFailed() {
|
||||||
showWarning("从VMS同步VPN服务器信息失败");
|
showWarning("从VMS同步VPN服务器信息失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showCustomizeDialog() {
|
||||||
|
/* @setView 装入自定义View ==> R.layout.dialog_customize
|
||||||
|
* 由于dialog_customize.xml只放置了一个EditView,因此和图8一样
|
||||||
|
* dialog_customize.xml可自定义更复杂的View
|
||||||
|
*/
|
||||||
|
View view = LayoutInflater.from(this).inflate(R.layout.layout_identity_dialog,null,false);
|
||||||
|
final AlertDialog dialog = new AlertDialog.Builder(this).setView(view).create();
|
||||||
|
//使得点击对话框外部不消失对话框
|
||||||
|
dialog.setCanceledOnTouchOutside(false);
|
||||||
|
// Button test_button=view.findViewById(R.id.text_button);
|
||||||
|
// test_button.setOnClickListener(v->{
|
||||||
|
// Toast.makeText(this, "You clicked test button", Toast.LENGTH_SHORT).show();
|
||||||
|
// dialog.dismiss();
|
||||||
|
// });
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
//设置dialog的位置、大小
|
||||||
|
Window dialogWindow = dialog.getWindow();
|
||||||
|
WindowManager.LayoutParams lp = dialogWindow.getAttributes();
|
||||||
|
lp.width = (int) (ScreenSizeUtils.getInstance(this).getScreenWidth() * 0.8f);
|
||||||
|
lp.height = (int) (ScreenSizeUtils.getInstance(this).getScreenHeight() * 0.5f);
|
||||||
|
dialogWindow.setAttributes(lp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
//回调函数
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
//导入身份
|
||||||
|
if (requestCode == 1) {
|
||||||
|
if (data.getStringArrayListExtra(Const.EXTRA_RESULT_SELECTION) != null) {
|
||||||
|
ArrayList<String> essFileList = data.getStringArrayListExtra(Const.EXTRA_RESULT_SELECTION);
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
System.out.println(essFileList.size());
|
||||||
|
System.out.println(essFileList);
|
||||||
|
for (String file :
|
||||||
|
essFileList) {
|
||||||
|
builder.append(file);
|
||||||
|
}
|
||||||
|
System.out.print(builder.toString());
|
||||||
|
String path=builder.toString();
|
||||||
|
new IdentityDump().importIdentity(path);
|
||||||
|
Toast.makeText(this, "export adress is "+path, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//导出身份
|
||||||
|
if(requestCode==2){
|
||||||
|
if (data.getStringArrayListExtra(Const.EXTRA_RESULT_SELECTION) != null) {
|
||||||
|
ArrayList<String> essFileList = data.getStringArrayListExtra(Const.EXTRA_RESULT_SELECTION);
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
System.out.println(essFileList.size());
|
||||||
|
System.out.println(essFileList);
|
||||||
|
for (String file :
|
||||||
|
essFileList) {
|
||||||
|
builder.append(file);
|
||||||
|
}
|
||||||
|
String path=builder.toString();
|
||||||
|
//new IdentityDump().importIdentity(path);
|
||||||
|
System.out.println(path);
|
||||||
|
new IdentityDump().exportIdentity(path);
|
||||||
|
Toast.makeText(this, "import address is "+path, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
package com.pkusz.min_vpn_client.utils;
|
package com.pkusz.min_vpn_client.utils.identity;
|
||||||
|
|
||||||
|
|
||||||
|
import com.pkusz.min_vpn_client.utils.KeyManager;
|
||||||
|
|
||||||
import security.SafeBag;
|
import security.SafeBag;
|
||||||
import util.FileUtil;
|
import util.FileUtil;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
|
||||||
@@ -79,6 +82,56 @@ public class IdentityDump {
|
|||||||
.getIdentity("/"+username,identityPath));
|
.getIdentity("/"+username,identityPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void importIdentity(String identityFilePath){
|
||||||
|
// 导入身份前查看是否有该用户身份
|
||||||
|
// System.out.println("试图取出未导入的用户身份: "+KeyManager.INSTANCE
|
||||||
|
// .getIdentity("/"+username,identityPath));
|
||||||
|
// 从本地文件中读取身份数据
|
||||||
|
|
||||||
|
byte[] identityBytes;
|
||||||
|
try {
|
||||||
|
identityBytes=FileUtil.readFile(identityFilePath);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 转成SafeBag
|
||||||
|
SafeBag safeBag=new SafeBag(identityBytes);
|
||||||
|
// 导入到本地身份数据库中
|
||||||
|
try {
|
||||||
|
KeyManager.INSTANCE.getKeyChain().importSafeBag(safeBag,"",true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
// 导入身份后查看是否有该用户身份
|
||||||
|
int last=identityFilePath.lastIndexOf("/");
|
||||||
|
String identityPath=identityFilePath.substring(0,last);
|
||||||
|
System.out.println("查看是否成功导入用户身份: "+KeyManager.INSTANCE
|
||||||
|
.getIdentity("/"+username,identityPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void exportIdentity(String identityPath){
|
||||||
|
String identityFilePath=identityPath+username+".export";
|
||||||
|
// 生成数据库身份
|
||||||
|
new IdentityDump().generateForeverIdentity(username,identityPath);
|
||||||
|
System.out.println("生成的用户身份: "+KeyManager.INSTANCE
|
||||||
|
.getIdentity("/"+username,identityPath));
|
||||||
|
// 导出数据库身份
|
||||||
|
SafeBag safeBag;
|
||||||
|
try {
|
||||||
|
safeBag=KeyManager.INSTANCE.getKeyChain().exportSafeBag("");
|
||||||
|
System.out.println(Arrays.toString(safeBag.getValue()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 将身份数据写入本地文件
|
||||||
|
try {
|
||||||
|
FileUtil.writeFile(identityFilePath,safeBag.getValue());
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.pkusz.min_vpn_client.utils.identity;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
public class ScreenSizeUtils {
|
||||||
|
private static ScreenSizeUtils instance = null;
|
||||||
|
private int screenWidth, screenHeight;
|
||||||
|
|
||||||
|
public static ScreenSizeUtils getInstance(Context mContext) {
|
||||||
|
if (instance == null) {
|
||||||
|
synchronized (ScreenSizeUtils.class) {
|
||||||
|
if (instance == null)
|
||||||
|
instance = new ScreenSizeUtils(mContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScreenSizeUtils(Context mContext) {
|
||||||
|
WindowManager manager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
|
||||||
|
DisplayMetrics dm = new DisplayMetrics();
|
||||||
|
manager.getDefaultDisplay().getMetrics(dm);
|
||||||
|
screenWidth = dm.widthPixels;// 获取屏幕分辨率宽度
|
||||||
|
screenHeight = dm.heightPixels;// 获取屏幕分辨率高度
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取屏幕宽度
|
||||||
|
public int getScreenWidth() {
|
||||||
|
return screenWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取屏幕高度
|
||||||
|
public int getScreenHeight() {
|
||||||
|
return screenHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/coordinator_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="选择文件目录"
|
||||||
|
android:id="@+id/hint_text"
|
||||||
|
/>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="33dp"
|
||||||
|
android:background="#EEEEEE"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imb_select_sdcard"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:rotation="90"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:src="@mipmap/ic_chevron_right_white_24dp"
|
||||||
|
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/breadcrumbs_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:paddingRight="10dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rcv_file_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="300dp"
|
||||||
|
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/confirm_button"
|
||||||
|
android:text="确定"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
After Width: | Height: | Size: 144 B |
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 27
|
||||||
|
buildToolsVersion "29.0.3"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 19
|
||||||
|
targetSdkVersion 27
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles 'consumer-rules.pro'
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
|
//Android 自定义性ActionBar
|
||||||
|
implementation 'com.github.ZLYang110:TitleBar:1.0.1'
|
||||||
|
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||||
|
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.4'
|
||||||
|
implementation 'com.github.bumptech.glide:glide:4.8.0'
|
||||||
|
implementation 'org.greenrobot:eventbus:3.1.1'
|
||||||
|
implementation 'com.android.support:design:27.1.0'
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.zlylib.fileselectorlib;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
public void useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||||
|
|
||||||
|
assertEquals("com.zlylib.fileselectorlib.test", appContext.getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.zlylib.fileselectorlib">
|
||||||
|
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<application>
|
||||||
|
<activity android:name=".ui.FileSelectorActivity" />
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.ess.file.provider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/provider_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
|
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package com.zlylib.fileselectorlib;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.zlylib.fileselectorlib.ui.FileSelectorActivity;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
public class FileSelector {
|
||||||
|
|
||||||
|
public static final int BY_NAME_ASC = 0;
|
||||||
|
public static final int BY_NAME_DESC = 1;
|
||||||
|
public static final int BY_TIME_ASC = 2;
|
||||||
|
public static final int BY_TIME_DESC = 3;
|
||||||
|
public static final int BY_SIZE_ASC = 4;
|
||||||
|
public static final int BY_SIZE_DESC = 5;
|
||||||
|
public static final int BY_EXTENSION_ASC = 6;
|
||||||
|
|
||||||
|
private final WeakReference<Context> mContext;
|
||||||
|
private final WeakReference<Fragment> mFragment;
|
||||||
|
|
||||||
|
private FileSelector(AppCompatActivity activity) {
|
||||||
|
this(activity,null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileSelector(Fragment fragment){
|
||||||
|
this( fragment.getActivity(),fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileSelector(Context mContext, Fragment mFragment) {
|
||||||
|
this.mContext = new WeakReference<>(mContext);
|
||||||
|
this.mFragment = new WeakReference<>(mFragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SelectCreator from(AppCompatActivity activity){
|
||||||
|
return new FileSelector(activity).initFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SelectCreator from(Fragment fragment){
|
||||||
|
return new FileSelector(fragment).initFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SelectCreator initFile(){
|
||||||
|
return new SelectCreator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Context getActivity() {
|
||||||
|
return mContext.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Fragment getFragment() {
|
||||||
|
return mFragment != null ? mFragment.get() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package com.zlylib.fileselectorlib;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.zlylib.fileselectorlib.ui.FileSelectorActivity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SelectCreator
|
||||||
|
* Created by zhangliyang on 2020/6/20.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public final class SelectCreator {
|
||||||
|
|
||||||
|
private FileSelector filePicker;
|
||||||
|
private SelectOptions selectOptions;
|
||||||
|
|
||||||
|
public SelectCreator(FileSelector filePicker ) {
|
||||||
|
selectOptions = SelectOptions.getCleanInstance();
|
||||||
|
this.filePicker = filePicker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelectCreator setMaxCount(int maxCount) {
|
||||||
|
selectOptions.maxCount = maxCount;
|
||||||
|
if (maxCount <= 1) {
|
||||||
|
selectOptions.maxCount = 1;
|
||||||
|
selectOptions.isSingle = true;
|
||||||
|
} else {
|
||||||
|
selectOptions.isSingle = false;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SelectCreator setTargetPath(String path){
|
||||||
|
selectOptions.targetPath = path;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public SelectCreator setFileTypes(String... fileTypes) {
|
||||||
|
selectOptions.mFileTypes = fileTypes;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelectCreator setSortType(int sortType) {
|
||||||
|
selectOptions.setSortType(sortType);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelectCreator isSingle() {
|
||||||
|
selectOptions.isSingle = true;
|
||||||
|
selectOptions.maxCount = 1;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelectCreator onlyShowFolder() {
|
||||||
|
selectOptions.setOnlyShowFolder(true);
|
||||||
|
selectOptions.setOnlySelectFolder(true);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public SelectCreator onlySelectFolder() {
|
||||||
|
selectOptions.setOnlySelectFolder(true);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public SelectCreator setTilteBg(int color) {
|
||||||
|
selectOptions.setTitleBg(color);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public SelectCreator setTitleColor(int color) {
|
||||||
|
selectOptions.setTitleColor(color);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public SelectCreator setTitleLiftColor(int color) {
|
||||||
|
selectOptions.setTitleLiftColor(color);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public SelectCreator setTitleRightColor(int color) {
|
||||||
|
selectOptions.setTitleRightColor(color);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/* public SelectCreator onlyShowImages() {
|
||||||
|
selectOptions.onlyShowImages = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelectCreator onlyShowVideos() {
|
||||||
|
selectOptions.onlyShowVideos = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public SelectCreator requestCode(int requestCode) {
|
||||||
|
selectOptions.request_code = requestCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
final Context activity = filePicker.getActivity();
|
||||||
|
if (activity == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setClass(activity, FileSelectorActivity.class);
|
||||||
|
Fragment fragment = filePicker.getFragment();
|
||||||
|
if (fragment != null) {
|
||||||
|
fragment.startActivityForResult(intent, selectOptions.request_code);
|
||||||
|
} else {
|
||||||
|
((AppCompatActivity)activity).startActivityForResult(intent, selectOptions.request_code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package com.zlylib.fileselectorlib;
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.zlylib.fileselectorlib.utils.FileUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SelectOptions
|
||||||
|
* Created by ZHANGLIYANG on 2020/6/20.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class SelectOptions {
|
||||||
|
|
||||||
|
|
||||||
|
public static final String defaultTargetPath = Environment.getExternalStorageDirectory() + "/";
|
||||||
|
|
||||||
|
public String[] mFileTypes;
|
||||||
|
public String mSortType;//排序类型
|
||||||
|
public boolean isSingle = false;//是否是单选
|
||||||
|
public int maxCount = 10;//最多选的个数
|
||||||
|
private boolean onlyShowFolder = false;//只显示文件夹
|
||||||
|
private boolean onlySelectFolder = false;//只选择文件夹
|
||||||
|
//public boolean onlyShowImages = false;//只显示图片
|
||||||
|
//public boolean onlyShowVideos = false;//只显示视频
|
||||||
|
public int request_code;//返回码
|
||||||
|
public String targetPath = defaultTargetPath;//设置默认目录
|
||||||
|
|
||||||
|
private int titleBg = 0;//标题背景颜色
|
||||||
|
private int titleColor = 0;//标题文字颜色
|
||||||
|
private int titleLiftColor = 0;//标题左边颜色
|
||||||
|
private int titleRightColor = 0;//标题右边颜色
|
||||||
|
|
||||||
|
|
||||||
|
public String[] getFileTypes() {
|
||||||
|
if (mFileTypes == null || mFileTypes.length == 0) {
|
||||||
|
return new String[]{};
|
||||||
|
}
|
||||||
|
return mFileTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSortType() {
|
||||||
|
if (TextUtils.isEmpty(mSortType)) {
|
||||||
|
return FileUtils.BY_NAME_ASC;
|
||||||
|
}
|
||||||
|
return Integer.valueOf(mSortType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSortType(int sortType) {
|
||||||
|
mSortType = String.valueOf(sortType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetPath() {
|
||||||
|
if (!new File(targetPath).exists()) {
|
||||||
|
File file = new File(defaultTargetPath);
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.mkdirs();
|
||||||
|
}
|
||||||
|
return defaultTargetPath;
|
||||||
|
}
|
||||||
|
return targetPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOnlyShowFolder() {
|
||||||
|
return onlyShowFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnlyShowFolder(boolean onlyShowFolder) {
|
||||||
|
this.onlyShowFolder = onlyShowFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOnlySelectFolder() {
|
||||||
|
return onlySelectFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnlySelectFolder(boolean onlySelectFolder) {
|
||||||
|
this.onlySelectFolder = onlySelectFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTitleBg() {
|
||||||
|
return titleBg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitleBg(int titleBg) {
|
||||||
|
this.titleBg = titleBg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTitleColor() {
|
||||||
|
return titleColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitleColor(int titleColor) {
|
||||||
|
this.titleColor = titleColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTitleLiftColor() {
|
||||||
|
return titleLiftColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitleLiftColor(int titleLiftColor) {
|
||||||
|
this.titleLiftColor = titleLiftColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTitleRightColor() {
|
||||||
|
return titleRightColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitleRightColor(int titleRightColor) {
|
||||||
|
this.titleRightColor = titleRightColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SelectOptions getInstance() {
|
||||||
|
return InstanceHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SelectOptions getCleanInstance() {
|
||||||
|
SelectOptions options = getInstance();
|
||||||
|
options.reset();
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset() {
|
||||||
|
mFileTypes = new String[]{};
|
||||||
|
mSortType = String.valueOf(FileUtils.BY_NAME_ASC);
|
||||||
|
isSingle = false;
|
||||||
|
maxCount = 10;
|
||||||
|
onlyShowFolder = false;
|
||||||
|
onlySelectFolder = false;
|
||||||
|
targetPath = defaultTargetPath;
|
||||||
|
titleBg = 0;//标题背景颜色
|
||||||
|
titleColor = 0;//标题文字颜色
|
||||||
|
titleLiftColor = 0;//标题左边颜色
|
||||||
|
titleRightColor = 0;//标题右边颜色
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class InstanceHolder {
|
||||||
|
private static final SelectOptions INSTANCE = new SelectOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.zlylib.fileselectorlib.adapter;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.chad.library.adapter.base.BaseQuickAdapter;
|
||||||
|
import com.chad.library.adapter.base.viewholder.BaseViewHolder;
|
||||||
|
import com.zlylib.fileselectorlib.R;
|
||||||
|
import com.zlylib.fileselectorlib.bean.BreadModel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BreadAdapter
|
||||||
|
* Created by zhangliyang on 2020/6/20.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class BreadAdapter extends BaseQuickAdapter<BreadModel, BaseViewHolder> {
|
||||||
|
|
||||||
|
public BreadAdapter(@Nullable List<BreadModel> data) {
|
||||||
|
super(R.layout.bread_item,data);
|
||||||
|
addChildClickViewIds(R.id.btn_bread);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void convert(BaseViewHolder helper, BreadModel item) {
|
||||||
|
helper.setText(R.id.btn_bread,item.getCurName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
package com.zlylib.fileselectorlib.adapter;
|
||||||
|
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.request.RequestOptions;
|
||||||
|
import com.chad.library.adapter.base.BaseQuickAdapter;
|
||||||
|
import com.chad.library.adapter.base.viewholder.BaseViewHolder;
|
||||||
|
import com.zlylib.fileselectorlib.R;
|
||||||
|
import com.zlylib.fileselectorlib.bean.EssFile;
|
||||||
|
import com.zlylib.fileselectorlib.utils.FileSizeUtil;
|
||||||
|
import com.zlylib.fileselectorlib.utils.FileUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FileListAdapter
|
||||||
|
* Created by zhangliyang on 2020/6/20.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class FileListAdapter extends BaseQuickAdapter<EssFile, BaseViewHolder> {
|
||||||
|
|
||||||
|
private onLoadFileCountListener loadFileCountListener;
|
||||||
|
|
||||||
|
public interface onLoadFileCountListener{
|
||||||
|
void onLoadFileCount(int posistion);
|
||||||
|
}
|
||||||
|
|
||||||
|
public onLoadFileCountListener getLoadFileCountListener() {
|
||||||
|
return loadFileCountListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoadFileCountListener(onLoadFileCountListener loadFileCountListener) {
|
||||||
|
this.loadFileCountListener = loadFileCountListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileListAdapter(@Nullable List<EssFile> data) {
|
||||||
|
super(R.layout.item_file_list, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void convert(BaseViewHolder helper, EssFile item) {
|
||||||
|
TextView textView = helper.getView(R.id.tv_item_file_list_desc);
|
||||||
|
if (item.isDirectory()) {
|
||||||
|
helper.setVisible(R.id.iv_item_file_select_right, true);
|
||||||
|
if(item.getChildFolderCount().equals("加载中")){
|
||||||
|
//查找数量
|
||||||
|
if(loadFileCountListener!=null){
|
||||||
|
loadFileCountListener.onLoadFileCount(helper.getAdapterPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
textView.setText(String.format(getContext().getString(R.string.folder_desc), item.getChildFileCount(),item.getChildFolderCount()));
|
||||||
|
} else {
|
||||||
|
helper.setVisible(R.id.iv_item_file_select_right, false);
|
||||||
|
textView.setText(String.format(getContext().getString(R.string.file_desc), FileUtils.getDateTime(item.getAbsolutePath()), FileSizeUtil.getAutoFileOrFilesSize(item.getFile())));
|
||||||
|
}
|
||||||
|
helper.setText(R.id.tv_item_file_list, item.getName());
|
||||||
|
if(item.isChecked()){
|
||||||
|
helper.setVisible(R.id.checkbox_item_file_list,true);
|
||||||
|
}else {
|
||||||
|
helper.setVisible(R.id.checkbox_item_file_list,false);
|
||||||
|
}
|
||||||
|
ImageView imageView = helper.getView(R.id.iv_item_file_select_left);
|
||||||
|
String fileNameExtension = FileUtils.getExtension(item.getName()).toLowerCase();
|
||||||
|
switch (fileNameExtension) {
|
||||||
|
case "apk":
|
||||||
|
imageView.setImageResource(R.mipmap.apk);
|
||||||
|
break;
|
||||||
|
case "avi":
|
||||||
|
imageView.setImageResource(R.mipmap.avi);
|
||||||
|
break;
|
||||||
|
case "doc":
|
||||||
|
case "docx":
|
||||||
|
imageView.setImageResource(R.mipmap.doc);
|
||||||
|
break;
|
||||||
|
case "exe":
|
||||||
|
imageView.setImageResource(R.mipmap.exe);
|
||||||
|
break;
|
||||||
|
case "flv":
|
||||||
|
imageView.setImageResource(R.mipmap.flv);
|
||||||
|
break;
|
||||||
|
case "gif":
|
||||||
|
RequestOptions options = new RequestOptions()
|
||||||
|
.centerCrop()
|
||||||
|
.placeholder(R.mipmap.gif);
|
||||||
|
Glide
|
||||||
|
.with(getContext())
|
||||||
|
.load(item.getAbsolutePath())
|
||||||
|
.apply(options)
|
||||||
|
.into(imageView);
|
||||||
|
break;
|
||||||
|
case "jpg":
|
||||||
|
case "jpeg":
|
||||||
|
case "png":
|
||||||
|
RequestOptions options2 = new RequestOptions()
|
||||||
|
.centerCrop()
|
||||||
|
.placeholder(R.mipmap.png);
|
||||||
|
Glide
|
||||||
|
.with(getContext())
|
||||||
|
.load(item.getAbsolutePath())
|
||||||
|
.apply(options2)
|
||||||
|
.into(imageView);
|
||||||
|
break;
|
||||||
|
case "mp3":
|
||||||
|
imageView.setImageResource(R.mipmap.mp3);
|
||||||
|
break;
|
||||||
|
case "mp4":
|
||||||
|
case "f4v":
|
||||||
|
imageView.setImageResource(R.mipmap.movie);
|
||||||
|
break;
|
||||||
|
case "pdf":
|
||||||
|
imageView.setImageResource(R.mipmap.pdf);
|
||||||
|
break;
|
||||||
|
case "ppt":
|
||||||
|
case "pptx":
|
||||||
|
imageView.setImageResource(R.mipmap.ppt);
|
||||||
|
break;
|
||||||
|
case "wav":
|
||||||
|
imageView.setImageResource(R.mipmap.wav);
|
||||||
|
break;
|
||||||
|
case "xls":
|
||||||
|
case "xlsx":
|
||||||
|
imageView.setImageResource(R.mipmap.xls);
|
||||||
|
break;
|
||||||
|
case "zip":
|
||||||
|
imageView.setImageResource(R.mipmap.zip);
|
||||||
|
break;
|
||||||
|
case "ext":
|
||||||
|
default:
|
||||||
|
if (item.isDirectory()) {
|
||||||
|
imageView.setImageResource(R.mipmap.folder);
|
||||||
|
} else {
|
||||||
|
imageView.setImageResource(R.mipmap.documents);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.zlylib.fileselectorlib.adapter;
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.chad.library.adapter.base.BaseQuickAdapter;
|
||||||
|
import com.chad.library.adapter.base.viewholder.BaseViewHolder;
|
||||||
|
import com.zlylib.fileselectorlib.R;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SelectSdcardAdapter
|
||||||
|
* Created by zhangliyang on 2020/6/22.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class SelectSdcardAdapter extends BaseQuickAdapter<String, BaseViewHolder> {
|
||||||
|
public SelectSdcardAdapter(@Nullable List<String> data) {
|
||||||
|
super(R.layout.item_select_sdcard,data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void convert(BaseViewHolder helper, String item) {
|
||||||
|
helper.setText(R.id.tv_item_select_sdcard,item);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package com.zlylib.fileselectorlib.bean;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BreadModel
|
||||||
|
* Created by zhangliyang on 2018/2/5.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class BreadModel {
|
||||||
|
|
||||||
|
/*当前Path*/
|
||||||
|
private String mCurPath;
|
||||||
|
|
||||||
|
/*之前的浏览位置,默认0*/
|
||||||
|
private int mPrePosition = 0;
|
||||||
|
|
||||||
|
/*当前面包屑的名称*/
|
||||||
|
private String mCurName;
|
||||||
|
|
||||||
|
public String getCurName() {
|
||||||
|
return mCurName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurName(String mCurName) {
|
||||||
|
this.mCurName = mCurName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPrePosition() {
|
||||||
|
return mPrePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrePosition(int mPrePosition) {
|
||||||
|
this.mPrePosition = mPrePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurPath() {
|
||||||
|
return mCurPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setmCurPath(String mCurPath) {
|
||||||
|
this.mCurPath = mCurPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取需要添加的路径列表
|
||||||
|
* @param oldList
|
||||||
|
* @param newList
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<BreadModel> getNewBreadModel(List<BreadModel> oldList, List<BreadModel> newList) {
|
||||||
|
List<BreadModel> newModelList = new ArrayList<>();
|
||||||
|
if (oldList == null || newList == null) {
|
||||||
|
return newModelList;
|
||||||
|
}
|
||||||
|
if (oldList.size() >= newList.size()) {
|
||||||
|
return newModelList;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < newList.size(); i++) {
|
||||||
|
if (i < oldList.size()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
newModelList.add(newList.get(i));
|
||||||
|
}
|
||||||
|
return newModelList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取路径List需要裁剪的位置
|
||||||
|
* @param oldList
|
||||||
|
* @param newList
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int getRemovedBreadModel(List<BreadModel> oldList, List<BreadModel> newList) {
|
||||||
|
int removeFirstPosition = 0;
|
||||||
|
if (oldList == null || newList == null) {
|
||||||
|
return removeFirstPosition;
|
||||||
|
}
|
||||||
|
if (oldList.size() <= newList.size()) {
|
||||||
|
return removeFirstPosition;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < oldList.size(); i++) {
|
||||||
|
if (i == newList.size()) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removeFirstPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
package com.zlylib.fileselectorlib.bean;
|
||||||
|
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
|
||||||
|
import com.zlylib.fileselectorlib.utils.FileUtils;
|
||||||
|
import com.zlylib.fileselectorlib.utils.MimeType;
|
||||||
|
import com.zlylib.fileselectorlib.utils.PathUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EssFile
|
||||||
|
* Created by zhangliyang on 2018/2/5.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class EssFile implements Parcelable {
|
||||||
|
|
||||||
|
public static final int CAPTURE = 0;
|
||||||
|
public static final int MEDIA = 1;
|
||||||
|
|
||||||
|
private String mFilePath;
|
||||||
|
private String mimeType;
|
||||||
|
private String childFolderCount = "加载中";
|
||||||
|
private String childFileCount = "加载中";
|
||||||
|
private boolean isChecked = false;
|
||||||
|
private boolean isExits = false;
|
||||||
|
private boolean isDirectory = false;
|
||||||
|
private boolean isFile = false;
|
||||||
|
private String mFileName;
|
||||||
|
private Uri uri;
|
||||||
|
|
||||||
|
private int itemType = MEDIA;
|
||||||
|
|
||||||
|
protected EssFile(Parcel in) {
|
||||||
|
mFilePath = in.readString();
|
||||||
|
mimeType = in.readString();
|
||||||
|
childFolderCount = in.readString();
|
||||||
|
childFileCount = in.readString();
|
||||||
|
isChecked = in.readByte() != 0;
|
||||||
|
isExits = in.readByte() != 0;
|
||||||
|
isDirectory = in.readByte() != 0;
|
||||||
|
isFile = in.readByte() != 0;
|
||||||
|
mFileName = in.readString();
|
||||||
|
uri = in.readParcelable(Uri.class.getClassLoader());
|
||||||
|
itemType = in.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<EssFile> CREATOR = new Creator<EssFile>() {
|
||||||
|
@Override
|
||||||
|
public EssFile createFromParcel(Parcel in) {
|
||||||
|
return new EssFile(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EssFile[] newArray(int size) {
|
||||||
|
return new EssFile[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public boolean isChecked() {
|
||||||
|
return isChecked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChecked(boolean checked) {
|
||||||
|
isChecked = checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EssFile(String path) {
|
||||||
|
mFilePath = path;
|
||||||
|
File file = new File(mFilePath);
|
||||||
|
if (file.exists()) {
|
||||||
|
isExits = true;
|
||||||
|
isDirectory = file.isDirectory();
|
||||||
|
isFile = file.isFile();
|
||||||
|
mFileName = file.getName();
|
||||||
|
}
|
||||||
|
mimeType = FileUtils.getMimeType(mFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EssFile(File file) {
|
||||||
|
mFilePath = file.getAbsolutePath();
|
||||||
|
if (file.exists()) {
|
||||||
|
isExits = true;
|
||||||
|
isDirectory = file.isDirectory();
|
||||||
|
isFile = file.isFile();
|
||||||
|
}
|
||||||
|
mimeType = FileUtils.getMimeType(file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public EssFile(long id, String mimeType) {
|
||||||
|
this.mimeType = mimeType;
|
||||||
|
Uri contentUri;
|
||||||
|
if (isImage()) {
|
||||||
|
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if (isVideo()) {
|
||||||
|
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else {
|
||||||
|
// ?
|
||||||
|
contentUri = MediaStore.Files.getContentUri("external");
|
||||||
|
}
|
||||||
|
this.uri = ContentUris.withAppendedId(contentUri, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uri getUri() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExits() {
|
||||||
|
return isExits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMimeType() {
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMimeType(String mimeType) {
|
||||||
|
this.mimeType = mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChildFolderCount() {
|
||||||
|
return childFolderCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChildFolderCount(String childFolderCount) {
|
||||||
|
this.childFolderCount = childFolderCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChildFileCount() {
|
||||||
|
return childFileCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChildFileCount(String childFileCount) {
|
||||||
|
this.childFileCount = childFileCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChildCounts(String childFileCount, String childFolderCount) {
|
||||||
|
this.childFileCount = childFileCount;
|
||||||
|
this.childFolderCount = childFolderCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public File getFile() {
|
||||||
|
return new File(mFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return new File(mFilePath).getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDirectory() {
|
||||||
|
return isDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFile() {
|
||||||
|
return isFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAbsolutePath() {
|
||||||
|
return mFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<EssFile> getEssFileList(List<File> files,boolean isSelectFolder) {
|
||||||
|
List<EssFile> essFileList = new ArrayList<>();
|
||||||
|
for (File file :files) {
|
||||||
|
if(isSelectFolder){
|
||||||
|
if(isFolder(file)){//只添加文件夹
|
||||||
|
essFileList.add(new EssFile(file));
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
essFileList.add(new EssFile(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return essFileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<EssFile> getEssFileList(Context context, Set<EssFile> essFileSet) {
|
||||||
|
ArrayList<EssFile> essFileArrayList = new ArrayList<>();
|
||||||
|
for (EssFile ess_file :
|
||||||
|
essFileSet) {
|
||||||
|
ess_file.mFilePath = PathUtils.getPath(context, ess_file.uri);
|
||||||
|
essFileArrayList.add(ess_file);
|
||||||
|
}
|
||||||
|
return essFileArrayList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<String> getFilePathList(ArrayList<EssFile> essFileArrayList){
|
||||||
|
ArrayList<String> resultList = new ArrayList<>();
|
||||||
|
for (EssFile essFile:essFileArrayList) {
|
||||||
|
resultList.add(essFile.getAbsolutePath());
|
||||||
|
}
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "EssFile{" +
|
||||||
|
"mFilePath='" + mFilePath + '\'' +
|
||||||
|
", mimeType='" + mimeType + '\'' +
|
||||||
|
", mFileName='" + mFileName + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
public static boolean isFolder(File file) {
|
||||||
|
return file.isDirectory();
|
||||||
|
}
|
||||||
|
public boolean isImage() {
|
||||||
|
if (mimeType == null) return false;
|
||||||
|
return mimeType.equals(MimeType.JPEG.toString())
|
||||||
|
|| mimeType.equals(MimeType.PNG.toString())
|
||||||
|
|| mimeType.equals(MimeType.GIF.toString())
|
||||||
|
|| mimeType.equals(MimeType.BMP.toString())
|
||||||
|
|| mimeType.equals(MimeType.WEBP.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isGif() {
|
||||||
|
if (mimeType == null) return false;
|
||||||
|
return mimeType.equals(MimeType.GIF.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVideo() {
|
||||||
|
if (mimeType == null) return false;
|
||||||
|
return mimeType.equals(MimeType.MPEG.toString())
|
||||||
|
|| mimeType.equals(MimeType.MP4.toString())
|
||||||
|
|| mimeType.equals(MimeType.QUICKTIME.toString())
|
||||||
|
|| mimeType.equals(MimeType.THREEGPP.toString())
|
||||||
|
|| mimeType.equals(MimeType.THREEGPP2.toString())
|
||||||
|
|| mimeType.equals(MimeType.MKV.toString())
|
||||||
|
|| mimeType.equals(MimeType.WEBM.toString())
|
||||||
|
|| mimeType.equals(MimeType.TS.toString())
|
||||||
|
|| mimeType.equals(MimeType.AVI.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItemType(int itemType) {
|
||||||
|
this.itemType = itemType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getItemType() {
|
||||||
|
return itemType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeString(mFilePath);
|
||||||
|
dest.writeString(mimeType);
|
||||||
|
dest.writeString(childFolderCount);
|
||||||
|
dest.writeString(childFileCount);
|
||||||
|
dest.writeByte((byte) (isChecked ? 1 : 0));
|
||||||
|
dest.writeByte((byte) (isExits ? 1 : 0));
|
||||||
|
dest.writeByte((byte) (isDirectory ? 1 : 0));
|
||||||
|
dest.writeByte((byte) (isFile ? 1 : 0));
|
||||||
|
dest.writeString(mFileName);
|
||||||
|
dest.writeParcelable(uri, flags);
|
||||||
|
dest.writeInt(itemType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof EssFile)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EssFile other = (EssFile) obj;
|
||||||
|
if (uri == null) {
|
||||||
|
return mFilePath.equalsIgnoreCase(other.getAbsolutePath());
|
||||||
|
} else {
|
||||||
|
return uri.equals(other.getUri());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = mFilePath != null ? mFilePath.hashCode() : 0;
|
||||||
|
result = 31 * result + (uri != null ? uri.hashCode() : 0);
|
||||||
|
result = 31 * result + itemType;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.zlylib.fileselectorlib.bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EssFileCountCallBack
|
||||||
|
* Created by zhangliyang on 2018/3/5.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface EssFileCountCallBack {
|
||||||
|
void onFindChildFileAndFolderCount(int position, String childFileCount, String childFolderCount);
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.zlylib.fileselectorlib.bean;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EssFileFilter
|
||||||
|
* 不显示隐藏文件、文件夹
|
||||||
|
*/
|
||||||
|
public class EssFileFilter implements FileFilter {
|
||||||
|
private String[] mTypes;
|
||||||
|
|
||||||
|
public EssFileFilter(String[] types) {
|
||||||
|
this.mTypes = types;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(File file) {
|
||||||
|
if (file.isDirectory() && !file.isHidden()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (mTypes != null && mTypes.length > 0) {
|
||||||
|
for (String mType : mTypes) {
|
||||||
|
if ((file.getName().endsWith(mType.toLowerCase()) || file.getName().endsWith(mType.toUpperCase())) && !file.isHidden()) {
|
||||||
|
// if (FileUtils.getMimeType(file.getAbsolutePath()).equalsIgnoreCase(MimeTypeMap.getSingleton().getMimeTypeFromExtension(mType))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.zlylib.fileselectorlib.bean;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EssFileListCallBack
|
||||||
|
* Created by zhangliyang on 2018/3/5.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface EssFileListCallBack {
|
||||||
|
void onFindFileList(String queryPath, List<EssFile> essFileList);
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package com.zlylib.fileselectorlib.core;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import com.zlylib.fileselectorlib.bean.EssFile;
|
||||||
|
import com.zlylib.fileselectorlib.bean.EssFileCountCallBack;
|
||||||
|
import com.zlylib.fileselectorlib.bean.EssFileFilter;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EssFileCountTask
|
||||||
|
* Created by zhangliyang on 2020/6/20.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class EssFileCountTask extends AsyncTask<Void,Void,Void>{
|
||||||
|
|
||||||
|
private int position;
|
||||||
|
private String queryPath;
|
||||||
|
private String[] types;
|
||||||
|
private EssFileCountCallBack countCallBack;
|
||||||
|
private int childFileCount = 0;
|
||||||
|
private int childFolderCount = 0;
|
||||||
|
private Boolean isSelectFolder = false;
|
||||||
|
|
||||||
|
public EssFileCountTask(int position, String queryPath, String[] types, Boolean isSelectFolder, EssFileCountCallBack countCallBack) {
|
||||||
|
this.position = position;
|
||||||
|
this.queryPath = queryPath;
|
||||||
|
this.types = types;
|
||||||
|
this.isSelectFolder = isSelectFolder;
|
||||||
|
this.countCallBack = countCallBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... voids) {
|
||||||
|
File file = new File(queryPath);
|
||||||
|
File[] files = file.listFiles(new EssFileFilter(types));
|
||||||
|
if(files == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<EssFile> fileList = EssFile.getEssFileList(Arrays.asList(files),isSelectFolder);
|
||||||
|
for (EssFile essFile :
|
||||||
|
fileList) {
|
||||||
|
if(essFile.isDirectory()){
|
||||||
|
childFolderCount++;
|
||||||
|
}else {
|
||||||
|
childFileCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Void aVoid) {
|
||||||
|
if(countCallBack!=null){
|
||||||
|
countCallBack.onFindChildFileAndFolderCount(position,String.valueOf(childFileCount),String.valueOf(childFolderCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package com.zlylib.fileselectorlib.core;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import com.zlylib.fileselectorlib.bean.EssFile;
|
||||||
|
import com.zlylib.fileselectorlib.bean.EssFileFilter;
|
||||||
|
import com.zlylib.fileselectorlib.bean.EssFileListCallBack;
|
||||||
|
import com.zlylib.fileselectorlib.utils.FileUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EssFileListTask
|
||||||
|
* Created by zhangliyang on 2018/3/5.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class EssFileListTask extends AsyncTask<Void,Void,List<EssFile>> {
|
||||||
|
|
||||||
|
private List<EssFile> mSelectedFileList;
|
||||||
|
private String queryPath;
|
||||||
|
private String[] types;
|
||||||
|
private int mSortType;
|
||||||
|
private EssFileListCallBack callBack;
|
||||||
|
private Boolean isSelectFolder = false;
|
||||||
|
|
||||||
|
public EssFileListTask(List<EssFile> mSelectedFileList, String queryPath, String[] types, int mSortType, Boolean isSelectFolder, EssFileListCallBack fileCallBack) {
|
||||||
|
this.mSelectedFileList = mSelectedFileList;
|
||||||
|
this.queryPath = queryPath;
|
||||||
|
this.types = types;
|
||||||
|
this.mSortType = mSortType;
|
||||||
|
this.isSelectFolder = isSelectFolder;
|
||||||
|
this.callBack = fileCallBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<EssFile> doInBackground(Void... voids) {
|
||||||
|
File file = new File(queryPath);
|
||||||
|
File[] files = file.listFiles(new EssFileFilter(types));
|
||||||
|
if(files == null){
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
List<File> fileList = Arrays.asList(files);
|
||||||
|
if(mSortType == FileUtils.BY_NAME_ASC){
|
||||||
|
Collections.sort(fileList, new FileUtils.SortByName());
|
||||||
|
}else if(mSortType == FileUtils.BY_NAME_DESC){
|
||||||
|
Collections.sort(fileList, new FileUtils.SortByName());
|
||||||
|
Collections.reverse(fileList);
|
||||||
|
}else if(mSortType == FileUtils.BY_TIME_ASC){
|
||||||
|
Collections.sort(fileList,new FileUtils.SortByTime());
|
||||||
|
}else if(mSortType == FileUtils.BY_TIME_DESC){
|
||||||
|
Collections.sort(fileList,new FileUtils.SortByTime());
|
||||||
|
Collections.reverse(fileList);
|
||||||
|
}else if(mSortType == FileUtils.BY_SIZE_ASC){
|
||||||
|
Collections.sort(fileList,new FileUtils.SortBySize());
|
||||||
|
}else if(mSortType == FileUtils.BY_SIZE_DESC){
|
||||||
|
Collections.sort(fileList,new FileUtils.SortBySize());
|
||||||
|
Collections.reverse(fileList);
|
||||||
|
}else if(mSortType == FileUtils.BY_EXTENSION_ASC){
|
||||||
|
Collections.sort(fileList,new FileUtils.SortByExtension());
|
||||||
|
}else if(mSortType == FileUtils.BY_EXTENSION_DESC){
|
||||||
|
Collections.sort(fileList,new FileUtils.SortByExtension());
|
||||||
|
Collections.reverse(fileList);
|
||||||
|
}
|
||||||
|
List<EssFile> tempFileList = EssFile.getEssFileList(fileList,isSelectFolder);
|
||||||
|
for (EssFile selectedFile :
|
||||||
|
mSelectedFileList) {
|
||||||
|
for (int i = 0; i < tempFileList.size(); i++) {
|
||||||
|
if (selectedFile.getAbsolutePath().equals(tempFileList.get(i).getAbsolutePath())) {
|
||||||
|
tempFileList.get(i).setChecked(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tempFileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
super.onPreExecute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(List<EssFile> essFileList) {
|
||||||
|
if(callBack!=null){
|
||||||
|
callBack.onFindFileList(queryPath,essFileList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,359 @@
|
|||||||
|
package com.zlylib.fileselectorlib.ui;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.PopupWindow;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
|
||||||
|
import com.chad.library.adapter.base.BaseQuickAdapter;
|
||||||
|
import com.chad.library.adapter.base.listener.OnItemChildClickListener;
|
||||||
|
import com.chad.library.adapter.base.listener.OnItemClickListener;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
import com.zlylib.fileselectorlib.R;
|
||||||
|
import com.zlylib.fileselectorlib.SelectOptions;
|
||||||
|
import com.zlylib.fileselectorlib.adapter.BreadAdapter;
|
||||||
|
import com.zlylib.fileselectorlib.adapter.FileListAdapter;
|
||||||
|
import com.zlylib.fileselectorlib.adapter.SelectSdcardAdapter;
|
||||||
|
import com.zlylib.fileselectorlib.bean.BreadModel;
|
||||||
|
import com.zlylib.fileselectorlib.bean.EssFile;
|
||||||
|
import com.zlylib.fileselectorlib.bean.EssFileCountCallBack;
|
||||||
|
import com.zlylib.fileselectorlib.bean.EssFileListCallBack;
|
||||||
|
import com.zlylib.fileselectorlib.core.EssFileCountTask;
|
||||||
|
import com.zlylib.fileselectorlib.core.EssFileListTask;
|
||||||
|
import com.zlylib.fileselectorlib.utils.Const;
|
||||||
|
import com.zlylib.fileselectorlib.utils.FileUtils;
|
||||||
|
import com.zlylib.fileselectorlib.utils.LogUtils;
|
||||||
|
import com.zlylib.titlebarlib.ActionBarCommon;
|
||||||
|
import com.zlylib.titlebarlib.OnActionBarChildClickListener;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class FileSelectorActivity extends AppCompatActivity implements OnItemClickListener, OnItemChildClickListener,
|
||||||
|
View.OnClickListener, FileListAdapter.onLoadFileCountListener, EssFileListCallBack, EssFileCountCallBack {
|
||||||
|
|
||||||
|
/*当前目录,默认是SD卡根目录*/
|
||||||
|
private String mCurFolder = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
|
||||||
|
/*是否刚才切换了SD卡路径*/
|
||||||
|
private boolean mHasChangeSdCard = false;
|
||||||
|
/*所有可访问存储设备列表*/
|
||||||
|
private List<String> mSdCardList;
|
||||||
|
private ActionBarCommon abc;
|
||||||
|
private RecyclerView mRecyclerView;
|
||||||
|
private RecyclerView mBreadRecyclerView;
|
||||||
|
private ImageView mImbSelectSdCard;
|
||||||
|
/**
|
||||||
|
* 已选中的文件列表
|
||||||
|
*/
|
||||||
|
private ArrayList<EssFile> mSelectedFileList = new ArrayList<>();// 全部信息列表
|
||||||
|
private ArrayList<String> mSelectedList = new ArrayList<>();//地址信息列表
|
||||||
|
/*当前选中排序方式的位置*/
|
||||||
|
private int mSelectSortTypeIndex = 0;
|
||||||
|
private BreadAdapter mBreadAdapter;
|
||||||
|
private FileListAdapter mAdapter;
|
||||||
|
|
||||||
|
private EssFileListTask essFileListTask;
|
||||||
|
private EssFileCountTask essFileCountTask;
|
||||||
|
|
||||||
|
private PopupWindow mSelectSdCardWindow;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_select_file);
|
||||||
|
mSdCardList = FileUtils.getAllSdPaths(this);
|
||||||
|
if (!mSdCardList.isEmpty()) {
|
||||||
|
mCurFolder = mSdCardList.get(0) + File.separator;
|
||||||
|
if(FileUtils.exist(SelectOptions.getInstance().getTargetPath())){
|
||||||
|
mCurFolder = SelectOptions.getInstance().getTargetPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
initUi();
|
||||||
|
initData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ResourceAsColor")
|
||||||
|
private void initUi() {
|
||||||
|
abc = findViewById(R.id.abc);
|
||||||
|
if (SelectOptions.getInstance().getTitleBg() != 0) {
|
||||||
|
abc.setBackgroundColor(getResources().getColor(SelectOptions.getInstance().getTitleBg()));
|
||||||
|
}
|
||||||
|
if (SelectOptions.getInstance().getTitleColor() != 0) {
|
||||||
|
abc.getTitleTextView().setTextColor(getResources().getColor(SelectOptions.getInstance().getTitleColor()));
|
||||||
|
}
|
||||||
|
if (SelectOptions.getInstance().getTitleLiftColor() != 0) {
|
||||||
|
abc.getLeftIconView().setColorFilter(getResources().getColor(SelectOptions.getInstance().getTitleLiftColor()));
|
||||||
|
}
|
||||||
|
if (SelectOptions.getInstance().getTitleRightColor() != 0) {
|
||||||
|
abc.getRightTextView().setTextColor(getResources().getColor(SelectOptions.getInstance().getTitleRightColor()));
|
||||||
|
}
|
||||||
|
abc.setOnLeftIconClickListener(new OnActionBarChildClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
onBackPressed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
abc.setOnRightTextClickListener(new OnActionBarChildClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (SelectOptions.getInstance().isOnlySelectFolder()) {
|
||||||
|
mSelectedList.add(mCurFolder);
|
||||||
|
}
|
||||||
|
//选中
|
||||||
|
if (mSelectedList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//不为空,向main activity返回数据
|
||||||
|
Intent result = new Intent();
|
||||||
|
result.putStringArrayListExtra(Const.EXTRA_RESULT_SELECTION, mSelectedList);
|
||||||
|
setResult(RESULT_OK, result);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (SelectOptions.getInstance().isOnlyShowFolder()) {
|
||||||
|
abc.getRightTextView().setText("选中");
|
||||||
|
}
|
||||||
|
mRecyclerView = findViewById(R.id.rcv_file_list);
|
||||||
|
mBreadRecyclerView = findViewById(R.id.breadcrumbs_view);
|
||||||
|
mImbSelectSdCard = findViewById(R.id.imb_select_sdcard);
|
||||||
|
mImbSelectSdCard.setOnClickListener(this);
|
||||||
|
if (!mSdCardList.isEmpty() && mSdCardList.size() > 1) {
|
||||||
|
mImbSelectSdCard.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||||
|
mAdapter = new FileListAdapter(new ArrayList<EssFile>());
|
||||||
|
mAdapter.setLoadFileCountListener(this);
|
||||||
|
mRecyclerView.setAdapter(mAdapter);
|
||||||
|
mAdapter.onAttachedToRecyclerView(mRecyclerView);
|
||||||
|
mAdapter.setOnItemClickListener(this);
|
||||||
|
List<BreadModel> breadModelList = FileUtils.getBreadModeListFromPath(mSdCardList, mCurFolder);
|
||||||
|
mBreadRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
|
||||||
|
mBreadAdapter = new BreadAdapter(breadModelList);
|
||||||
|
mBreadRecyclerView.setAdapter(mBreadAdapter);
|
||||||
|
mBreadAdapter.onAttachedToRecyclerView(mBreadRecyclerView);
|
||||||
|
mBreadAdapter.setOnItemChildClickListener(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initData() {
|
||||||
|
executeListTask(mSelectedFileList, mCurFolder, SelectOptions.getInstance().getFileTypes(), SelectOptions.getInstance().getSortType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeListTask(List<EssFile> essFileList, String queryPath, String[] types, int sortType) {
|
||||||
|
essFileListTask = new EssFileListTask(essFileList, queryPath, types, sortType, SelectOptions.getInstance().isOnlyShowFolder(), this);
|
||||||
|
essFileListTask.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
if (view.getId() == R.id.imb_select_sdcard) {
|
||||||
|
showPopupWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示选择SdCard的PopupWindow
|
||||||
|
* 点击其他区域隐藏,阴影
|
||||||
|
*/
|
||||||
|
private void showPopupWindow() {
|
||||||
|
if (mSelectSdCardWindow != null) {
|
||||||
|
mSelectSdCardWindow.showAsDropDown(mImbSelectSdCard);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
View popView = LayoutInflater.from(this).inflate(R.layout.pop_select_sdcard, null);
|
||||||
|
mSelectSdCardWindow = new PopupWindow(popView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
mSelectSdCardWindow.setFocusable(true);
|
||||||
|
mSelectSdCardWindow.setOutsideTouchable(true);
|
||||||
|
RecyclerView recyclerView = popView.findViewById(R.id.rcv_pop_select_sdcard);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||||
|
final SelectSdcardAdapter adapter = new SelectSdcardAdapter(FileUtils.getAllSdCardList(mSdCardList));
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
adapter.onAttachedToRecyclerView(recyclerView);
|
||||||
|
adapter.setOnItemClickListener(new OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(BaseQuickAdapter adapterIn, View view, int position) {
|
||||||
|
mSelectSdCardWindow.dismiss();
|
||||||
|
mHasChangeSdCard = true;
|
||||||
|
executeListTask(mSelectedFileList, FileUtils.getChangeSdCard(adapter.getData().get(position), mSdCardList), SelectOptions.getInstance().getFileTypes(), SelectOptions.getInstance().getSortType());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mSelectSdCardWindow.showAsDropDown(mImbSelectSdCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
|
||||||
|
if (adapter.equals(mBreadAdapter) && view.getId() == R.id.btn_bread) {
|
||||||
|
//点击某个路径时
|
||||||
|
String queryPath = FileUtils.getBreadModelListByPosition(mSdCardList, mBreadAdapter.getData(), position);
|
||||||
|
if (mCurFolder.equals(queryPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
executeListTask(mSelectedFileList, queryPath, SelectOptions.getInstance().getFileTypes(), SelectOptions.getInstance().getSortType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClick(BaseQuickAdapter adapter, View view, final int position) {
|
||||||
|
if (adapter.equals(mAdapter)) {
|
||||||
|
EssFile item = mAdapter.getData().get(position);
|
||||||
|
|
||||||
|
if (item.isDirectory()) {
|
||||||
|
//点击文件夹
|
||||||
|
//保存当前的垂直滚动位置
|
||||||
|
mBreadAdapter.getData().get(mBreadAdapter.getData().size() - 1).setPrePosition(mRecyclerView.computeVerticalScrollOffset());
|
||||||
|
executeListTask(mSelectedFileList, mCurFolder + item.getName() + File.separator, SelectOptions.getInstance().getFileTypes(), SelectOptions.getInstance().getSortType());
|
||||||
|
} else {
|
||||||
|
//选中某文件后,判断是否只能选择文件夹
|
||||||
|
if (SelectOptions.getInstance().isOnlySelectFolder()) {
|
||||||
|
if (!item.getFile().isDirectory()) {
|
||||||
|
Snackbar.make(mRecyclerView, "您只能选择文件夹", Snackbar.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// if (SelectOptions.getInstance().isSingle) {
|
||||||
|
// mSelectedFileList.add(item);
|
||||||
|
// mSelectedList.add(item.getAbsolutePath());
|
||||||
|
// Intent result = new Intent();
|
||||||
|
// result.putParcelableArrayListExtra(Const.EXTRA_RESULT_SELECTION, mSelectedFileList);
|
||||||
|
// setResult(RESULT_OK, result);
|
||||||
|
// super.onBackPressed();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
//选中某文件后,判断是否单选
|
||||||
|
if (SelectOptions.getInstance().isSingle) {
|
||||||
|
mSelectedFileList.add(item);
|
||||||
|
mSelectedList.add(item.getAbsolutePath());
|
||||||
|
Intent result = new Intent();
|
||||||
|
result.putStringArrayListExtra(Const.EXTRA_RESULT_SELECTION, mSelectedList);
|
||||||
|
setResult(RESULT_OK, result);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
if (mAdapter.getData().get(position).isChecked()) {
|
||||||
|
int index = findFileIndex(item);
|
||||||
|
if (index != -1) {
|
||||||
|
mSelectedFileList.remove(index);
|
||||||
|
mSelectedList.remove(index);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mSelectedFileList.size() >= SelectOptions.getInstance().maxCount) {
|
||||||
|
//超出最大可选择数量后
|
||||||
|
Snackbar.make(mRecyclerView, "您最多只能选择" + SelectOptions.getInstance().maxCount + "个。", Snackbar.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mSelectedFileList.add(item);
|
||||||
|
mSelectedList.add(item.getAbsolutePath());
|
||||||
|
}
|
||||||
|
mAdapter.getData().get(position).setChecked(!mAdapter.getData().get(position).isChecked());
|
||||||
|
// mAdapter.notifyItemChanged(position, "");
|
||||||
|
mAdapter.notifyDataSetChanged();
|
||||||
|
abc.getRightTextView().setText(String.format(getString(R.string.selected_file_count), String.valueOf(mSelectedFileList.size()), String.valueOf(SelectOptions.getInstance().maxCount)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找文件位置
|
||||||
|
*/
|
||||||
|
private int findFileIndex(EssFile item) {
|
||||||
|
for (int i = 0; i < mSelectedFileList.size(); i++) {
|
||||||
|
if (mSelectedFileList.get(i).getAbsolutePath().equals(item.getAbsolutePath())) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFileCount(int posistion) {
|
||||||
|
essFileCountTask = new EssFileCountTask(posistion, mAdapter.getData().get(posistion).getAbsolutePath(), SelectOptions.getInstance().getFileTypes(), SelectOptions.getInstance().isOnlyShowFolder(), this);
|
||||||
|
essFileCountTask.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找到文件列表后
|
||||||
|
*
|
||||||
|
* @param queryPath 查询路径
|
||||||
|
* @param fileList 文件列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onFindFileList(String queryPath, List<EssFile> fileList) {
|
||||||
|
if (fileList.isEmpty()) {
|
||||||
|
mAdapter.setEmptyView(R.layout.empty_file_list);
|
||||||
|
}
|
||||||
|
mCurFolder = queryPath;
|
||||||
|
mAdapter.setNewInstance(fileList);
|
||||||
|
List<BreadModel> breadModelList = FileUtils.getBreadModeListFromPath(mSdCardList, mCurFolder);
|
||||||
|
if (mHasChangeSdCard) {
|
||||||
|
mBreadAdapter.setNewInstance(breadModelList);
|
||||||
|
mHasChangeSdCard = false;
|
||||||
|
} else {
|
||||||
|
if (breadModelList.size() > mBreadAdapter.getData().size()) {
|
||||||
|
//新增
|
||||||
|
List<BreadModel> newList = BreadModel.getNewBreadModel(mBreadAdapter.getData(), breadModelList);
|
||||||
|
mBreadAdapter.addData(newList);
|
||||||
|
} else {
|
||||||
|
//减少
|
||||||
|
int removePosition = BreadModel.getRemovedBreadModel(mBreadAdapter.getData(), breadModelList);
|
||||||
|
if (removePosition > 0) {
|
||||||
|
mBreadAdapter.setNewData(mBreadAdapter.getData().subList(0, removePosition));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mBreadRecyclerView.smoothScrollToPosition(mBreadAdapter.getItemCount() - 1);
|
||||||
|
//先让其滚动到顶部,然后再scrollBy,滚动到之前保存的位置
|
||||||
|
mRecyclerView.scrollToPosition(0);
|
||||||
|
int scrollYPosition = mBreadAdapter.getData().get(mBreadAdapter.getData().size() - 1).getPrePosition();
|
||||||
|
//恢复之前的滚动位置
|
||||||
|
mRecyclerView.scrollBy(0, scrollYPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFindChildFileAndFolderCount(int position, String childFileCount, String childFolderCount) {
|
||||||
|
mAdapter.getData().get(position).setChildCounts(childFileCount, childFolderCount);
|
||||||
|
// mAdapter.notifyItemChanged(position, "childCountChanges");
|
||||||
|
mAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
if (!FileUtils.canBackParent(mCurFolder, mSdCardList)) {
|
||||||
|
super.onBackPressed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
executeListTask(mSelectedFileList, new File(mCurFolder).getParentFile().getAbsolutePath() + File.separator, SelectOptions.getInstance().getFileTypes(), SelectOptions.getInstance().getSortType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
// TODO: 2019/3/12 暂时移除
|
||||||
|
// EventBus.getDefault().unregister(this);
|
||||||
|
if (essFileListTask != null) {
|
||||||
|
essFileListTask.cancel(true);
|
||||||
|
}
|
||||||
|
if (essFileCountTask != null) {
|
||||||
|
essFileCountTask.cancel(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package com.zlylib.fileselectorlib.utils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Const
|
||||||
|
* Created by zhangliyang on 2020/6/20.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Const {
|
||||||
|
|
||||||
|
public static Map<String, String> mimeTypeMap = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
mimeTypeMap = new HashMap<>();
|
||||||
|
mimeTypeMap.put("apk","application/vnd.android.package-archive");
|
||||||
|
mimeTypeMap.put("asf", "video/x-ms-asf");
|
||||||
|
mimeTypeMap.put("avi", "video/x-msvideo");
|
||||||
|
mimeTypeMap.put("bin", "application/octet-stream");
|
||||||
|
mimeTypeMap.put("bmp", "image/bmp");
|
||||||
|
mimeTypeMap.put("c", "text/plain");
|
||||||
|
mimeTypeMap.put("class", "application/octet-stream");
|
||||||
|
mimeTypeMap.put("conf", "text/plain");
|
||||||
|
mimeTypeMap.put("cpp", "text/plain");
|
||||||
|
mimeTypeMap.put("doc", "application/msword");
|
||||||
|
mimeTypeMap.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
|
||||||
|
mimeTypeMap.put("xls", "application/vnd.ms-excel");
|
||||||
|
mimeTypeMap.put("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||||
|
mimeTypeMap.put("exe", "application/octet-stream");
|
||||||
|
mimeTypeMap.put("gif", "image/gif");
|
||||||
|
mimeTypeMap.put("gtar", "application/x-gtar");
|
||||||
|
mimeTypeMap.put("gz", "application/x-gzip");
|
||||||
|
mimeTypeMap.put("h", "text/plain");
|
||||||
|
mimeTypeMap.put("htm", "text/html");
|
||||||
|
mimeTypeMap.put("html", "text/html");
|
||||||
|
mimeTypeMap.put("jar", "application/java-archive");
|
||||||
|
mimeTypeMap.put("java", "text/plain");
|
||||||
|
mimeTypeMap.put("jpeg", "image/jpeg");
|
||||||
|
mimeTypeMap.put("jpg", "image/jpeg");
|
||||||
|
mimeTypeMap.put("js", "application/x-javascript");
|
||||||
|
mimeTypeMap.put("log", "text/plain");
|
||||||
|
mimeTypeMap.put("m3u", "audio/x-mpegurl");
|
||||||
|
mimeTypeMap.put("m4a", "audio/mp4a-latm");
|
||||||
|
mimeTypeMap.put("m4b", "audio/mp4a-latm");
|
||||||
|
mimeTypeMap.put("m4p", "audio/mp4a-latm");
|
||||||
|
mimeTypeMap.put("m4u", "video/vnd.mpegurl");
|
||||||
|
mimeTypeMap.put("m4v", "video/x-m4v");
|
||||||
|
mimeTypeMap.put("mov", "video/quicktime");
|
||||||
|
mimeTypeMap.put("mp2", "audio/x-mpeg");
|
||||||
|
mimeTypeMap.put("mp3", "audio/mpeg");
|
||||||
|
mimeTypeMap.put("mp4", "video/mp4");
|
||||||
|
mimeTypeMap.put("mpc", "application/vnd.mpohun.certificate");
|
||||||
|
mimeTypeMap.put("mpe", "video/mpeg");
|
||||||
|
mimeTypeMap.put("mpeg", "video/mpeg");
|
||||||
|
mimeTypeMap.put("mpg", "video/mpeg");
|
||||||
|
mimeTypeMap.put("mpg4", "video/mp4");
|
||||||
|
mimeTypeMap.put("mpga", "audio/mpeg");
|
||||||
|
mimeTypeMap.put("msg", "application/vnd.ms-outlook");
|
||||||
|
mimeTypeMap.put("ogg", "audio/ogg");
|
||||||
|
mimeTypeMap.put("pdf", "application/pdf");
|
||||||
|
mimeTypeMap.put("png", "image/png");
|
||||||
|
mimeTypeMap.put("pps", "application/vnd.ms-powerpoint");
|
||||||
|
mimeTypeMap.put("ppt", "application/vnd.ms-powerpoint");
|
||||||
|
mimeTypeMap.put("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
|
||||||
|
mimeTypeMap.put("prop", "text/plain");
|
||||||
|
mimeTypeMap.put("rc", "text/plain");
|
||||||
|
mimeTypeMap.put("rmvb", "audio/x-pn-realaudio");
|
||||||
|
mimeTypeMap.put("rtf", "application/rtf");
|
||||||
|
mimeTypeMap.put("sh", "text/plain");
|
||||||
|
mimeTypeMap.put("tar", "application/x-tar");
|
||||||
|
mimeTypeMap.put("tgz", "application/x-compressed");
|
||||||
|
mimeTypeMap.put("txt", "text/plain");
|
||||||
|
mimeTypeMap.put("wav", "audio/x-wav");
|
||||||
|
mimeTypeMap.put("wma", "audio/x-ms-wma");
|
||||||
|
mimeTypeMap.put("wmv", "audio/x-ms-wmv");
|
||||||
|
mimeTypeMap.put("wps", "application/vnd.ms-works");
|
||||||
|
mimeTypeMap.put("xml", "text/plain");
|
||||||
|
mimeTypeMap.put("z", "application/x-compress");
|
||||||
|
mimeTypeMap.put("zip", "application/x-zip-compressed");
|
||||||
|
mimeTypeMap.put("", "*/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*文件类型*/
|
||||||
|
public static final String EXTRA_KEY_FILE_TYPE = "EXTRA_KEY_FILE_TYPE";
|
||||||
|
/*排序类型*/
|
||||||
|
public static final String EXTRA_KEY_SORT_TYPE = "EXTRA_KEY_SORT_TYPE";
|
||||||
|
/*是否多选*/
|
||||||
|
public static final String EXTRA_KEY_IS_SINGLE = "EXTRA_KEY_IS_SINGLE";
|
||||||
|
/*最大可选择数量*/
|
||||||
|
public static final String EXTRA_KEY_MAX_COUNT = "EXTRA_KEY_MAX_COUNT";
|
||||||
|
public static final String EXTRA_KEY_OnSelectFileListener = "EXTRA_KEY_OnSelectFileListener";
|
||||||
|
|
||||||
|
public static final String EXTRA_RESULT_SELECTION = "extra_result_selection";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,636 @@
|
|||||||
|
package com.zlylib.fileselectorlib.utils;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.NinePatchDrawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
|
||||||
|
import androidx.annotation.ColorInt;
|
||||||
|
import androidx.annotation.FloatRange;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据类型转换、单位转换
|
||||||
|
*
|
||||||
|
* @author zhangliyang
|
||||||
|
* @since 2014-4-18
|
||||||
|
*/
|
||||||
|
public class ConvertUtils {
|
||||||
|
public static final long GB = 1073741824;
|
||||||
|
public static final long MB = 1048576;
|
||||||
|
public static final long KB = 1024;
|
||||||
|
|
||||||
|
public static int toInt(Object obj) {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(obj.toString());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int toInt(byte[] bytes) {
|
||||||
|
int result = 0;
|
||||||
|
byte abyte;
|
||||||
|
for (int i = 0; i < bytes.length; i++) {
|
||||||
|
abyte = bytes[i];
|
||||||
|
result += (abyte & 0xFF) << (8 * i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int toShort(byte first, byte second) {
|
||||||
|
return (first << 8) + (second & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long toLong(Object obj) {
|
||||||
|
try {
|
||||||
|
return Long.parseLong(obj.toString());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return -1L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float toFloat(Object obj) {
|
||||||
|
try {
|
||||||
|
return Float.parseFloat(obj.toString());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return -1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* int占4字节
|
||||||
|
*
|
||||||
|
* @param i the
|
||||||
|
* @return byte [ ]
|
||||||
|
*/
|
||||||
|
public static byte[] toByteArray(int i) {
|
||||||
|
// byte[] bytes = new byte[4];
|
||||||
|
// bytes[0] = (byte) (0xff & i);
|
||||||
|
// bytes[1] = (byte) ((0xff00 & i) >> 8);
|
||||||
|
// bytes[2] = (byte) ((0xff0000 & i) >> 16);
|
||||||
|
// bytes[3] = (byte) ((0xff000000 & i) >> 24);
|
||||||
|
// return bytes;
|
||||||
|
return ByteBuffer.allocate(4).putInt(i).array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toByteArray(String hexData, boolean isHex) {
|
||||||
|
if (hexData == null || hexData.equals("")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!isHex) {
|
||||||
|
return hexData.getBytes();
|
||||||
|
}
|
||||||
|
hexData = hexData.replaceAll("\\s+", "");
|
||||||
|
String hexDigits = "0123456789ABCDEF";
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream(
|
||||||
|
hexData.length() / 2);
|
||||||
|
// 将每2位16进制整数组装成一个字节
|
||||||
|
for (int i = 0; i < hexData.length(); i += 2) {
|
||||||
|
baos.write((hexDigits.indexOf(hexData.charAt(i)) << 4 | hexDigits
|
||||||
|
.indexOf(hexData.charAt(i + 1))));
|
||||||
|
}
|
||||||
|
byte[] bytes = baos.toByteArray();
|
||||||
|
try {
|
||||||
|
baos.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.warn(e);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toHexString(String str) {
|
||||||
|
if (TextUtils.isEmpty(str))
|
||||||
|
return "";
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
byte[] bytes = str.getBytes();
|
||||||
|
for (byte aByte : bytes) {
|
||||||
|
builder.append(Integer.toHexString(0xFF & aByte));
|
||||||
|
builder.append(" ");
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To hex string string.
|
||||||
|
*
|
||||||
|
* @param bytes the bytes
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
|
public static String toHexString(byte... bytes) {
|
||||||
|
char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6',
|
||||||
|
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||||
|
// 参见:http://www.oschina.net/code/snippet_116768_9019
|
||||||
|
char[] buffer = new char[bytes.length * 2];
|
||||||
|
for (int i = 0, j = 0; i < bytes.length; ++i) {
|
||||||
|
int u = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];//转无符号整型
|
||||||
|
buffer[j++] = DIGITS[u >>> 4];
|
||||||
|
buffer[j++] = DIGITS[u & 0xf];
|
||||||
|
}
|
||||||
|
return new String(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To hex string string.
|
||||||
|
*
|
||||||
|
* @param num the num
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
|
public static String toHexString(int num) {
|
||||||
|
String hexString = Integer.toHexString(num);
|
||||||
|
LogUtils.verbose(String.format(Locale.CHINA, "%d to hex string is %s", num, hexString));
|
||||||
|
return hexString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To binary string string.
|
||||||
|
*
|
||||||
|
* @param bytes the bytes
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
|
public static String toBinaryString(byte... bytes) {
|
||||||
|
char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6',
|
||||||
|
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||||
|
// 参见:http://www.oschina.net/code/snippet_116768_9019
|
||||||
|
char[] buffer = new char[bytes.length * 8];
|
||||||
|
for (int i = 0, j = 0; i < bytes.length; ++i) {
|
||||||
|
int u = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];//转无符号整型
|
||||||
|
buffer[j++] = DIGITS[(u >>> 7) & 0x1];
|
||||||
|
buffer[j++] = DIGITS[(u >>> 6) & 0x1];
|
||||||
|
buffer[j++] = DIGITS[(u >>> 5) & 0x1];
|
||||||
|
buffer[j++] = DIGITS[(u >>> 4) & 0x1];
|
||||||
|
buffer[j++] = DIGITS[(u >>> 3) & 0x1];
|
||||||
|
buffer[j++] = DIGITS[(u >>> 2) & 0x1];
|
||||||
|
buffer[j++] = DIGITS[(u >>> 1) & 0x1];
|
||||||
|
buffer[j++] = DIGITS[u & 0x1];
|
||||||
|
}
|
||||||
|
return new String(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To binary string string.
|
||||||
|
*
|
||||||
|
* @param num the num
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
|
public static String toBinaryString(int num) {
|
||||||
|
String binaryString = Integer.toBinaryString(num);
|
||||||
|
LogUtils.verbose(String.format(Locale.CHINA, "%d to binary string is %s", num, binaryString));
|
||||||
|
return binaryString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toSlashString(String str) {
|
||||||
|
String result = "";
|
||||||
|
char[] chars = str.toCharArray();
|
||||||
|
for (char chr : chars) {
|
||||||
|
if (chr == '"' || chr == '\'' || chr == '\\') {
|
||||||
|
result += "\\";//符合“"”“'”“\”这三个符号的前面加一个“\”
|
||||||
|
}
|
||||||
|
result += chr;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T[] toArray(List<T> list) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (T[]) list.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> toList(T[] array) {
|
||||||
|
return Arrays.asList(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toString(Object[] objects) {
|
||||||
|
return Arrays.deepToString(objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toString(Object[] objects, String tag) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (Object object : objects) {
|
||||||
|
sb.append(object);
|
||||||
|
sb.append(tag);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toByteArray(InputStream is) {
|
||||||
|
if (is == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
byte[] buff = new byte[100];
|
||||||
|
while (true) {
|
||||||
|
int len = is.read(buff, 0, 100);
|
||||||
|
if (len == -1) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
os.write(buff, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte[] bytes = os.toByteArray();
|
||||||
|
os.close();
|
||||||
|
is.close();
|
||||||
|
return bytes;
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.warn(e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toByteArray(Bitmap bitmap) {
|
||||||
|
if (bitmap == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
// 将Bitmap压缩成PNG编码,质量为100%存储,除了PNG还有很多常见格式,如jpeg等。
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
|
||||||
|
byte[] bytes = os.toByteArray();
|
||||||
|
try {
|
||||||
|
os.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.warn(e);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Bitmap toBitmap(byte[] bytes, int width, int height) {
|
||||||
|
Bitmap bitmap = null;
|
||||||
|
if (bytes.length != 0) {
|
||||||
|
try {
|
||||||
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
|
// 不进行图片抖动处理
|
||||||
|
options.inDither = false;
|
||||||
|
// 设置让解码器以最佳方式解码
|
||||||
|
options.inPreferredConfig = null;
|
||||||
|
if (width > 0 && height > 0) {
|
||||||
|
options.outWidth = width;
|
||||||
|
options.outHeight = height;
|
||||||
|
}
|
||||||
|
bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
|
||||||
|
bitmap.setDensity(96);// 96 dpi
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Bitmap toBitmap(byte[] bytes) {
|
||||||
|
return toBitmap(bytes, -1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Drawable转换为Bitmap
|
||||||
|
* 参考:http://kylines.iteye.com/blog/1660184
|
||||||
|
*/
|
||||||
|
public static Bitmap toBitmap(Drawable drawable) {
|
||||||
|
if (drawable instanceof BitmapDrawable) {
|
||||||
|
return ((BitmapDrawable) drawable).getBitmap();
|
||||||
|
} else if (drawable instanceof ColorDrawable) {
|
||||||
|
//color
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
canvas.drawColor(((ColorDrawable) drawable).getColor());
|
||||||
|
return bitmap;
|
||||||
|
} else if (drawable instanceof NinePatchDrawable) {
|
||||||
|
//.9.png
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
|
||||||
|
drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
|
||||||
|
drawable.draw(canvas);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从第三方文件选择器获取路径。
|
||||||
|
* 参见:http://blog.csdn.net/zbjdsbj/article/details/42387551
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||||
|
public static String toPath(Context context, Uri uri) {
|
||||||
|
if (uri == null) {
|
||||||
|
LogUtils.verbose("uri is null");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
LogUtils.verbose("uri: " + uri.toString());
|
||||||
|
String path = uri.getPath();
|
||||||
|
String scheme = uri.getScheme();
|
||||||
|
String authority = uri.getAuthority();
|
||||||
|
//是否是4.4及以上版本
|
||||||
|
boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||||
|
// DocumentProvider
|
||||||
|
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
|
||||||
|
String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
String[] split = docId.split(":");
|
||||||
|
String type = split[0];
|
||||||
|
Uri contentUri = null;
|
||||||
|
switch (authority) {
|
||||||
|
// ExternalStorageProvider
|
||||||
|
case "com.android.externalstorage.documents":
|
||||||
|
if ("primary".equalsIgnoreCase(type)) {
|
||||||
|
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
// DownloadsProvider
|
||||||
|
case "com.android.providers.downloads.documents":
|
||||||
|
contentUri = ContentUris.withAppendedId(
|
||||||
|
Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
|
||||||
|
return _queryPathFromMediaStore(context, contentUri, null, null);
|
||||||
|
// MediaProvider
|
||||||
|
case "com.android.providers.media.documents":
|
||||||
|
if ("image".equals(type)) {
|
||||||
|
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("video".equals(type)) {
|
||||||
|
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("audio".equals(type)) {
|
||||||
|
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
}
|
||||||
|
String selection = "_id=?";
|
||||||
|
String[] selectionArgs = new String[]{split[1]};
|
||||||
|
return _queryPathFromMediaStore(context, contentUri, selection, selectionArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// MediaStore (and general)
|
||||||
|
else {
|
||||||
|
if ("content".equalsIgnoreCase(scheme)) {
|
||||||
|
// Return the remote address
|
||||||
|
if (authority.equals("com.google.android.apps.photos.content")) {
|
||||||
|
return uri.getLastPathSegment();
|
||||||
|
}
|
||||||
|
return _queryPathFromMediaStore(context, uri, null, null);
|
||||||
|
}
|
||||||
|
// File
|
||||||
|
else if ("file".equalsIgnoreCase(scheme)) {
|
||||||
|
return uri.getPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LogUtils.verbose("uri to path: " + path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String _queryPathFromMediaStore(Context context, Uri uri, String selection, String[] selectionArgs) {
|
||||||
|
String filePath = null;
|
||||||
|
try {
|
||||||
|
String[] projection = {MediaStore.Images.Media.DATA};
|
||||||
|
Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
|
||||||
|
if (cursor != null) {
|
||||||
|
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
filePath = cursor.getString(column_index);
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
}
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把view转化为bitmap(截图)
|
||||||
|
* 参见:http://www.cnblogs.com/lee0oo0/p/3355468.html
|
||||||
|
*/
|
||||||
|
public static Bitmap toBitmap(View view) {
|
||||||
|
int width = view.getWidth();
|
||||||
|
int height = view.getHeight();
|
||||||
|
if (view instanceof ListView) {
|
||||||
|
height = 0;
|
||||||
|
// 获取listView实际高度
|
||||||
|
ListView listView = (ListView) view;
|
||||||
|
for (int i = 0; i < listView.getChildCount(); i++) {
|
||||||
|
height += listView.getChildAt(i).getHeight();
|
||||||
|
}
|
||||||
|
} else if (view instanceof ScrollView) {
|
||||||
|
height = 0;
|
||||||
|
// 获取scrollView实际高度
|
||||||
|
ScrollView scrollView = (ScrollView) view;
|
||||||
|
for (int i = 0; i < scrollView.getChildCount(); i++) {
|
||||||
|
height += scrollView.getChildAt(i).getHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
view.setDrawingCacheEnabled(true);
|
||||||
|
view.clearFocus();
|
||||||
|
view.setPressed(false);
|
||||||
|
boolean willNotCache = view.willNotCacheDrawing();
|
||||||
|
view.setWillNotCacheDrawing(false);
|
||||||
|
// Reset the drawing cache background color to fully transparent for the duration of this operation
|
||||||
|
int color = view.getDrawingCacheBackgroundColor();
|
||||||
|
view.setDrawingCacheBackgroundColor(Color.WHITE);//截图去黑色背景(透明像素)
|
||||||
|
if (color != Color.WHITE) {
|
||||||
|
view.destroyDrawingCache();
|
||||||
|
}
|
||||||
|
view.buildDrawingCache();
|
||||||
|
Bitmap cacheBitmap = view.getDrawingCache();
|
||||||
|
if (cacheBitmap == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
canvas.drawBitmap(cacheBitmap, 0, 0, null);
|
||||||
|
canvas.save(Canvas.ALL_SAVE_FLAG);
|
||||||
|
canvas.restore();
|
||||||
|
if (!bitmap.isRecycled()) {
|
||||||
|
LogUtils.verbose("recycle bitmap: " + bitmap.toString());
|
||||||
|
bitmap.recycle();
|
||||||
|
}
|
||||||
|
// Restore the view
|
||||||
|
view.destroyDrawingCache();
|
||||||
|
view.setWillNotCacheDrawing(willNotCache);
|
||||||
|
view.setDrawingCacheBackgroundColor(color);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Drawable toDrawable(Bitmap bitmap) {
|
||||||
|
return bitmap == null ? null : new BitmapDrawable(Resources.getSystem(), bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toByteArray(Drawable drawable) {
|
||||||
|
return toByteArray(toBitmap(drawable));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Drawable toDrawable(byte[] bytes) {
|
||||||
|
return toDrawable(toBitmap(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dp转换为px
|
||||||
|
*/
|
||||||
|
public static int toPx(Context context, float dpValue) {
|
||||||
|
final float scale = context.getResources().getDisplayMetrics().density;
|
||||||
|
int pxValue = (int) (dpValue * scale + 0.5f);
|
||||||
|
LogUtils.verbose(dpValue + " dp == " + pxValue + " px");
|
||||||
|
return pxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* px转换为dp
|
||||||
|
*/
|
||||||
|
public static int toDp(Context context, float pxValue) {
|
||||||
|
final float scale = context.getResources().getDisplayMetrics().density;
|
||||||
|
int dpValue = (int) (pxValue / scale + 0.5f);
|
||||||
|
LogUtils.verbose(pxValue + " px == " + dpValue + " dp");
|
||||||
|
return dpValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* px转换为sp
|
||||||
|
*/
|
||||||
|
public static int toSp(Context context, float pxValue) {
|
||||||
|
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
|
||||||
|
int spValue = (int) (pxValue / fontScale + 0.5f);
|
||||||
|
LogUtils.verbose(pxValue + " px == " + spValue + " sp");
|
||||||
|
return spValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toGbk(String str) {
|
||||||
|
try {
|
||||||
|
return new String(str.getBytes("utf-8"), "gbk");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
LogUtils.warn(e);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toFileSizeString(long fileSize) {
|
||||||
|
DecimalFormat df = new DecimalFormat("0.00");
|
||||||
|
String fileSizeString;
|
||||||
|
if (fileSize < KB) {
|
||||||
|
fileSizeString = fileSize + "B";
|
||||||
|
} else if (fileSize < MB) {
|
||||||
|
fileSizeString = df.format((double) fileSize / KB) + "K";
|
||||||
|
} else if (fileSize < GB) {
|
||||||
|
fileSizeString = df.format((double) fileSize / MB) + "M";
|
||||||
|
} else {
|
||||||
|
fileSizeString = df.format((double) fileSize / GB) + "G";
|
||||||
|
}
|
||||||
|
return fileSizeString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toString(InputStream is, String charset) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
try {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset));
|
||||||
|
while (true) {
|
||||||
|
String line = reader.readLine();
|
||||||
|
if (line == null) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
sb.append(line).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
is.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toString(InputStream is) {
|
||||||
|
return toString(is, "utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int toDarkenColor(@ColorInt int color) {
|
||||||
|
return toDarkenColor(color, 0.8f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int toDarkenColor(@ColorInt int color, @FloatRange(from = 0f, to = 1f) float value) {
|
||||||
|
float[] hsv = new float[3];
|
||||||
|
Color.colorToHSV(color, hsv);
|
||||||
|
hsv[2] *= value;//HSV指Hue、Saturation、Value,即色调、饱和度和亮度,此处表示修改亮度
|
||||||
|
return Color.HSVToColor(hsv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为6位十六进制颜色代码,不含“#”
|
||||||
|
*/
|
||||||
|
public static String toColorString(@ColorInt int color) {
|
||||||
|
return toColorString(color, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为6位十六进制颜色代码,不含“#”
|
||||||
|
*/
|
||||||
|
public static String toColorString(@ColorInt int color, boolean includeAlpha) {
|
||||||
|
String alpha = Integer.toHexString(Color.alpha(color));
|
||||||
|
String red = Integer.toHexString(Color.red(color));
|
||||||
|
String green = Integer.toHexString(Color.green(color));
|
||||||
|
String blue = Integer.toHexString(Color.blue(color));
|
||||||
|
if (alpha.length() == 1) {
|
||||||
|
alpha = "0" + alpha;
|
||||||
|
}
|
||||||
|
if (red.length() == 1) {
|
||||||
|
red = "0" + red;
|
||||||
|
}
|
||||||
|
if (green.length() == 1) {
|
||||||
|
green = "0" + green;
|
||||||
|
}
|
||||||
|
if (blue.length() == 1) {
|
||||||
|
blue = "0" + blue;
|
||||||
|
}
|
||||||
|
String colorString;
|
||||||
|
if (includeAlpha) {
|
||||||
|
colorString = alpha + red + green + blue;
|
||||||
|
LogUtils.verbose(String.format(Locale.CHINA, "%d to color string is %s", color, colorString));
|
||||||
|
} else {
|
||||||
|
colorString = red + green + blue;
|
||||||
|
LogUtils.verbose(String.format(Locale.CHINA, "%d to color string is %s%s%s%s, exclude alpha is %s", color, alpha, red, green, blue, colorString));
|
||||||
|
}
|
||||||
|
return colorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对TextView、Button等设置不同状态时其文字颜色。
|
||||||
|
* 参见:http://blog.csdn.net/sodino/article/details/6797821
|
||||||
|
* Modified by liyujiang at 2015.08.13
|
||||||
|
*/
|
||||||
|
public static ColorStateList toColorStateList(@ColorInt int normalColor, @ColorInt int pressedColor,
|
||||||
|
@ColorInt int focusedColor, @ColorInt int unableColor) {
|
||||||
|
int[] colors = new int[]{pressedColor, focusedColor, normalColor, focusedColor, unableColor, normalColor};
|
||||||
|
int[][] states = new int[6][];
|
||||||
|
states[0] = new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled};
|
||||||
|
states[1] = new int[]{android.R.attr.state_enabled, android.R.attr.state_focused};
|
||||||
|
states[2] = new int[]{android.R.attr.state_enabled};
|
||||||
|
states[3] = new int[]{android.R.attr.state_focused};
|
||||||
|
states[4] = new int[]{android.R.attr.state_window_focused};
|
||||||
|
states[5] = new int[]{};
|
||||||
|
return new ColorStateList(states, colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ColorStateList toColorStateList(@ColorInt int normalColor, @ColorInt int pressedColor) {
|
||||||
|
return toColorStateList(normalColor, pressedColor, pressedColor, normalColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
package com.zlylib.fileselectorlib.utils;
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.annotation.IntDef;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期时间工具类
|
||||||
|
*
|
||||||
|
* @author zhangliyang
|
||||||
|
* @since 2015/8/5
|
||||||
|
*/
|
||||||
|
public class DateUtils extends android.text.format.DateUtils {
|
||||||
|
public static final int Second = 0;
|
||||||
|
public static final int Minute = 1;
|
||||||
|
public static final int Hour = 2;
|
||||||
|
public static final int Day = 3;
|
||||||
|
|
||||||
|
@IntDef(value = {Second, Minute, Hour, Day})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface DifferenceMode {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long calculateDifferentSecond(Date startDate, Date endDate) {
|
||||||
|
return calculateDifference(startDate, endDate, Second);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long calculateDifferentMinute(Date startDate, Date endDate) {
|
||||||
|
return calculateDifference(startDate, endDate, Minute);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long calculateDifferentHour(Date startDate, Date endDate) {
|
||||||
|
return calculateDifference(startDate, endDate, Hour);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long calculateDifferentDay(Date startDate, Date endDate) {
|
||||||
|
return calculateDifference(startDate, endDate, Day);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long calculateDifferentSecond(long startTimeMillis, long endTimeMillis) {
|
||||||
|
return calculateDifference(startTimeMillis, endTimeMillis, Second);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long calculateDifferentMinute(long startTimeMillis, long endTimeMillis) {
|
||||||
|
return calculateDifference(startTimeMillis, endTimeMillis, Minute);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long calculateDifferentHour(long startTimeMillis, long endTimeMillis) {
|
||||||
|
return calculateDifference(startTimeMillis, endTimeMillis, Hour);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long calculateDifferentDay(long startTimeMillis, long endTimeMillis) {
|
||||||
|
return calculateDifference(startTimeMillis, endTimeMillis, Day);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算两个时间戳之间相差的时间戳数
|
||||||
|
*/
|
||||||
|
public static long calculateDifference(long startTimeMillis, long endTimeMillis, @DifferenceMode int mode) {
|
||||||
|
return calculateDifference(new Date(startTimeMillis), new Date(endTimeMillis), mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算两个日期之间相差的时间戳数
|
||||||
|
*/
|
||||||
|
public static long calculateDifference(Date startDate, Date endDate, @DifferenceMode int mode) {
|
||||||
|
long[] different = calculateDifference(startDate, endDate);
|
||||||
|
if (mode == Minute) {
|
||||||
|
return different[2];
|
||||||
|
} else if (mode == Hour) {
|
||||||
|
return different[1];
|
||||||
|
} else if (mode == Day) {
|
||||||
|
return different[0];
|
||||||
|
} else {
|
||||||
|
return different[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long[] calculateDifference(Date startDate, Date endDate) {
|
||||||
|
return calculateDifference(endDate.getTime() - startDate.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long[] calculateDifference(long differentMilliSeconds) {
|
||||||
|
long secondsInMilli = 1000;//1s==1000ms
|
||||||
|
long minutesInMilli = secondsInMilli * 60;
|
||||||
|
long hoursInMilli = minutesInMilli * 60;
|
||||||
|
long daysInMilli = hoursInMilli * 24;
|
||||||
|
long elapsedDays = differentMilliSeconds / daysInMilli;
|
||||||
|
differentMilliSeconds = differentMilliSeconds % daysInMilli;
|
||||||
|
long elapsedHours = differentMilliSeconds / hoursInMilli;
|
||||||
|
differentMilliSeconds = differentMilliSeconds % hoursInMilli;
|
||||||
|
long elapsedMinutes = differentMilliSeconds / minutesInMilli;
|
||||||
|
differentMilliSeconds = differentMilliSeconds % minutesInMilli;
|
||||||
|
long elapsedSeconds = differentMilliSeconds / secondsInMilli;
|
||||||
|
LogUtils.verbose(String.format(Locale.CHINA, "different: %d ms, %d days, %d hours, %d minutes, %d seconds",
|
||||||
|
differentMilliSeconds, elapsedDays, elapsedHours, elapsedMinutes, elapsedSeconds));
|
||||||
|
return new long[]{elapsedDays, elapsedHours, elapsedMinutes, elapsedSeconds};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算每月的天数
|
||||||
|
*/
|
||||||
|
public static int calculateDaysInMonth(int month) {
|
||||||
|
return calculateDaysInMonth(0, month);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据年份及月份计算每月的天数
|
||||||
|
*/
|
||||||
|
public static int calculateDaysInMonth(int year, int month) {
|
||||||
|
// 添加大小月月份并将其转换为list,方便之后的判断
|
||||||
|
String[] bigMonths = {"1", "3", "5", "7", "8", "10", "12"};
|
||||||
|
String[] littleMonths = {"4", "6", "9", "11"};
|
||||||
|
List<String> bigList = Arrays.asList(bigMonths);
|
||||||
|
List<String> littleList = Arrays.asList(littleMonths);
|
||||||
|
// 判断大小月及是否闰年,用来确定"日"的数据
|
||||||
|
if (bigList.contains(String.valueOf(month))) {
|
||||||
|
return 31;
|
||||||
|
} else if (littleList.contains(String.valueOf(month))) {
|
||||||
|
return 30;
|
||||||
|
} else {
|
||||||
|
if (year <= 0) {
|
||||||
|
return 29;
|
||||||
|
}
|
||||||
|
// 是否闰年
|
||||||
|
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
|
||||||
|
return 29;
|
||||||
|
} else {
|
||||||
|
return 28;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 月日时分秒,0-9前补0
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static String fillZero(int number) {
|
||||||
|
return number < 10 ? "0" + number : "" + number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 截取掉前缀0以便转换为整数
|
||||||
|
*
|
||||||
|
* @see #fillZero(int)
|
||||||
|
*/
|
||||||
|
public static int trimZero(@NonNull String text) {
|
||||||
|
try {
|
||||||
|
if (text.startsWith("0")) {
|
||||||
|
text = text.substring(1);
|
||||||
|
}
|
||||||
|
return Integer.parseInt(text);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
LogUtils.warn(e);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 功能:判断日期是否和当前date对象在同一天。
|
||||||
|
* 参见:http://www.cnblogs.com/myzhijie/p/3330970.html
|
||||||
|
*
|
||||||
|
* @param date 比较的日期
|
||||||
|
* @return boolean 如果在返回true,否则返回false。
|
||||||
|
*/
|
||||||
|
public static boolean isSameDay(Date date) {
|
||||||
|
if (date == null) {
|
||||||
|
throw new IllegalArgumentException("date is null");
|
||||||
|
}
|
||||||
|
Calendar nowCalendar = Calendar.getInstance();
|
||||||
|
Calendar newCalendar = Calendar.getInstance();
|
||||||
|
newCalendar.setTime(date);
|
||||||
|
return (nowCalendar.get(Calendar.ERA) == newCalendar.get(Calendar.ERA) &&
|
||||||
|
nowCalendar.get(Calendar.YEAR) == newCalendar.get(Calendar.YEAR) &&
|
||||||
|
nowCalendar.get(Calendar.DAY_OF_YEAR) == newCalendar.get(Calendar.DAY_OF_YEAR));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将yyyy-MM-dd HH:mm:ss字符串转换成日期<br/>
|
||||||
|
*
|
||||||
|
* @param dateStr 时间字符串
|
||||||
|
* @param dataFormat 当前时间字符串的格式。
|
||||||
|
* @return Date 日期 ,转换异常时返回null。
|
||||||
|
*/
|
||||||
|
public static Date parseDate(String dateStr, String dataFormat) {
|
||||||
|
try {
|
||||||
|
SimpleDateFormat dateFormat = new SimpleDateFormat(dataFormat, Locale.PRC);
|
||||||
|
Date date = dateFormat.parse(dateStr);
|
||||||
|
return new Date(date.getTime());
|
||||||
|
} catch (ParseException e) {
|
||||||
|
LogUtils.warn(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将yyyy-MM-dd HH:mm:ss字符串转换成日期<br/>
|
||||||
|
*
|
||||||
|
* @param dateStr yyyy-MM-dd HH:mm:ss字符串
|
||||||
|
* @return Date 日期 ,转换异常时返回null。
|
||||||
|
*/
|
||||||
|
public static Date parseDate(String dateStr) {
|
||||||
|
return parseDate(dateStr, "yyyy-MM-dd HH:mm:ss");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFileLastModifyTime(long time) {
|
||||||
|
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CHINA);
|
||||||
|
return formatter.format(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将指定的日期转换为一定格式的字符串
|
||||||
|
*/
|
||||||
|
public static String formatDate(Date date, String format) {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.PRC);
|
||||||
|
return sdf.format(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将当前日期转换为一定格式的字符串
|
||||||
|
*/
|
||||||
|
public static String formatDate(String format) {
|
||||||
|
return formatDate(Calendar.getInstance(Locale.CHINA).getTime(), format);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
package com.zlylib.fileselectorlib.utils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FileSizeUtil 计算文件大小,自动转换为b,kb,mb等
|
||||||
|
* Created by 李波 on 2018/2/7.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class FileSizeUtil {
|
||||||
|
private static final String TAG = FileSizeUtil.class.getSimpleName();
|
||||||
|
|
||||||
|
public static final int SIZETYPE_B = 1;//获取文件大小单位为B的double值
|
||||||
|
public static final int SIZETYPE_KB = 2;//获取文件大小单位为KB的double值
|
||||||
|
public static final int SIZETYPE_MB = 3;//获取文件大小单位为MB的double值
|
||||||
|
public static final int SIZETYPE_GB = 4;//获取文件大小单位为GB的double值
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件指定文件的指定单位的大小
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @param sizeType 获取大小的类型1为B、2为KB、3为MB、4为GB
|
||||||
|
* @return double值的大小
|
||||||
|
*/
|
||||||
|
public static double getFileOrFilesSize(String filePath, int sizeType) {
|
||||||
|
File file = new File(filePath);
|
||||||
|
long blockSize = 0;
|
||||||
|
try {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
blockSize = getFileSizes(file);
|
||||||
|
} else {
|
||||||
|
blockSize = getFileSize(file);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return FormetFileSize(blockSize, sizeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用此方法自动计算指定文件或指定文件夹的大小
|
||||||
|
*
|
||||||
|
* @return 计算好的带B、KB、MB、GB的字符串
|
||||||
|
*/
|
||||||
|
public static String getAutoFileOrFilesSize(File file) {
|
||||||
|
long blockSize = 0;
|
||||||
|
try {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
blockSize = getFileSizes(file);
|
||||||
|
} else {
|
||||||
|
blockSize = file.length();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return FormetFileSize(blockSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定文件大小
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private static long getFileSize(File file) throws Exception {
|
||||||
|
long size = 0;
|
||||||
|
if (file.exists()) {
|
||||||
|
FileInputStream fis = null;
|
||||||
|
fis = new FileInputStream(file);
|
||||||
|
size = fis.available();
|
||||||
|
} else {
|
||||||
|
file.createNewFile();
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定文件夹
|
||||||
|
*
|
||||||
|
* @param f
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private static long getFileSizes(File f) throws Exception {
|
||||||
|
long size = 0;
|
||||||
|
File flist[] = f.listFiles();
|
||||||
|
for (int i = 0; i < flist.length; i++) {
|
||||||
|
if (flist[i].isDirectory()) {
|
||||||
|
size = size + getFileSizes(flist[i]);
|
||||||
|
} else {
|
||||||
|
size = size + getFileSize(flist[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换文件大小
|
||||||
|
*
|
||||||
|
* @param fileS
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static String FormetFileSize(long fileS) {
|
||||||
|
DecimalFormat df = new DecimalFormat("#.00");
|
||||||
|
String fileSizeString = "";
|
||||||
|
String wrongSize = "0B";
|
||||||
|
if (fileS == 0) {
|
||||||
|
return wrongSize;
|
||||||
|
}
|
||||||
|
if (fileS < 1024) {
|
||||||
|
fileSizeString = df.format((double) fileS) + "B";
|
||||||
|
} else if (fileS < 1048576) {
|
||||||
|
fileSizeString = df.format((double) fileS / 1024) + "KB";
|
||||||
|
} else if (fileS < 1073741824) {
|
||||||
|
fileSizeString = df.format((double) fileS / 1048576) + "MB";
|
||||||
|
} else {
|
||||||
|
fileSizeString = df.format((double) fileS / 1073741824) + "GB";
|
||||||
|
}
|
||||||
|
return fileSizeString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换文件大小,指定转换的类型
|
||||||
|
*
|
||||||
|
* @param fileS
|
||||||
|
* @param sizeType
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static double FormetFileSize(long fileS, int sizeType) {
|
||||||
|
DecimalFormat df = new DecimalFormat("#.00");
|
||||||
|
double fileSizeLong = 0;
|
||||||
|
switch (sizeType) {
|
||||||
|
case SIZETYPE_B:
|
||||||
|
fileSizeLong = Double.valueOf(df.format((double) fileS));
|
||||||
|
break;
|
||||||
|
case SIZETYPE_KB:
|
||||||
|
fileSizeLong = Double.valueOf(df.format((double) fileS / 1024));
|
||||||
|
break;
|
||||||
|
case SIZETYPE_MB:
|
||||||
|
fileSizeLong = Double.valueOf(df.format((double) fileS / 1048576));
|
||||||
|
break;
|
||||||
|
case SIZETYPE_GB:
|
||||||
|
fileSizeLong = Double.valueOf(df.format((double) fileS / 1073741824));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return fileSizeLong;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,946 @@
|
|||||||
|
package com.zlylib.fileselectorlib.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.os.storage.StorageManager;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.annotation.IntDef;
|
||||||
|
|
||||||
|
import com.zlylib.fileselectorlib.bean.BreadModel;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件处理
|
||||||
|
* <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
* <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
*
|
||||||
|
* @author zhangliyang
|
||||||
|
* @since 2014-4-18
|
||||||
|
*/
|
||||||
|
public final class FileUtils {
|
||||||
|
public static final int BY_NAME_ASC = 0;
|
||||||
|
public static final int BY_NAME_DESC = 1;
|
||||||
|
public static final int BY_TIME_ASC = 2;
|
||||||
|
public static final int BY_TIME_DESC = 3;
|
||||||
|
public static final int BY_SIZE_ASC = 4;
|
||||||
|
public static final int BY_SIZE_DESC = 5;
|
||||||
|
public static final int BY_EXTENSION_ASC = 6;
|
||||||
|
public static final int BY_EXTENSION_DESC = 7;
|
||||||
|
|
||||||
|
@IntDef(value = {
|
||||||
|
BY_NAME_ASC,//按名称
|
||||||
|
BY_NAME_DESC,
|
||||||
|
BY_TIME_ASC,//按最后修改时间
|
||||||
|
BY_TIME_DESC,
|
||||||
|
BY_SIZE_ASC,//按大小
|
||||||
|
BY_SIZE_DESC,
|
||||||
|
BY_EXTENSION_ASC,//按文件扩展名
|
||||||
|
BY_EXTENSION_DESC
|
||||||
|
})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface SortType {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将目录分隔符统一为平台默认的分隔符,并为目录结尾添加分隔符
|
||||||
|
*/
|
||||||
|
public static String separator(String path) {
|
||||||
|
String separator = File.separator;
|
||||||
|
path = path.replace("\\", separator);
|
||||||
|
if (!path.endsWith(separator)) {
|
||||||
|
path += separator;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从路径名中获取面包屑导航列表
|
||||||
|
*
|
||||||
|
* @param mSdCardList mSdCardList
|
||||||
|
* @param path 路径名
|
||||||
|
* @return 面包屑导航列表
|
||||||
|
*/
|
||||||
|
public static List<BreadModel> getBreadModeListFromPath(List<String> mSdCardList, String path) {
|
||||||
|
List<String> pathList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < mSdCardList.size(); i++) {
|
||||||
|
LogUtils.debug("getBreadModeListFromPath","--"+mSdCardList.get(i));
|
||||||
|
if (i == 0) {
|
||||||
|
//内部存储设备
|
||||||
|
path = path.replace(mSdCardList.get(i), "/内部存储设备");
|
||||||
|
} else {
|
||||||
|
path = path.replace(mSdCardList.get(i), "/SD卡" + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(path)) {
|
||||||
|
String[] a = path.substring(1, path.length()).split("/");
|
||||||
|
pathList = Arrays.asList(a);
|
||||||
|
}
|
||||||
|
List<BreadModel> breadModelList = new ArrayList<>();
|
||||||
|
for (String str : pathList) {
|
||||||
|
LogUtils.debug("getBreadModeListFromPath",str);
|
||||||
|
BreadModel breadModel = new BreadModel();
|
||||||
|
breadModel.setCurName(str);
|
||||||
|
breadModelList.add(breadModel);
|
||||||
|
}
|
||||||
|
return breadModelList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击某个位置的面包屑导航,获取到面包屑导航列表
|
||||||
|
*
|
||||||
|
* @param mSdCardList mSdCardList
|
||||||
|
* @param breadModelList 原列表
|
||||||
|
* @param position 点击位置
|
||||||
|
* @return 新列表
|
||||||
|
*/
|
||||||
|
public static String getBreadModelListByPosition(List<String> mSdCardList, List<BreadModel> breadModelList, int position) {
|
||||||
|
StringBuilder result = new StringBuilder("/");
|
||||||
|
for (int i = 0; i < breadModelList.size(); i++) {
|
||||||
|
if (position >= i) {
|
||||||
|
result.append(breadModelList.get(i).getCurName()).append(File.separator);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String resultStr = result.toString();
|
||||||
|
if (resultStr.startsWith("/内部存储设备")) {
|
||||||
|
resultStr = resultStr.replace("/内部存储设备", mSdCardList.get(0));
|
||||||
|
} else if (resultStr.startsWith("/SD卡")) {
|
||||||
|
resultStr = resultStr.replace("/SD卡" + String.valueOf(resultStr.charAt(4)), mSdCardList.get(Integer.valueOf(String.valueOf(resultStr.charAt(4)))));
|
||||||
|
}
|
||||||
|
return resultStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换SD卡路径
|
||||||
|
* @param sdCardName
|
||||||
|
* @param mSdCardList
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getChangeSdCard(String sdCardName, List<String> mSdCardList){
|
||||||
|
if(TextUtils.isEmpty(sdCardName)){
|
||||||
|
return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
|
||||||
|
}
|
||||||
|
if(sdCardName.startsWith("内部存储设备")){
|
||||||
|
return mSdCardList.get(0) + File.separator;
|
||||||
|
}
|
||||||
|
if(sdCardName.startsWith("SD卡")){
|
||||||
|
return mSdCardList.get(Integer.valueOf(String.valueOf(sdCardName.charAt(3)))) + File.separator;
|
||||||
|
}
|
||||||
|
return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否可以返回上一级目录,true可以返回上级目录,false不可以返回,直接finish
|
||||||
|
*/
|
||||||
|
public static boolean canBackParent(String curFolder, List<String> sdCardList) {
|
||||||
|
for (String sdcard :
|
||||||
|
sdCardList) {
|
||||||
|
if (curFolder.equals(sdcard + File.separator)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void closeSilently(Closeable c) {
|
||||||
|
if (c == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
c.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出指定目录下的所有子目录
|
||||||
|
*/
|
||||||
|
public static File[] listDirs(String startDirPath, String[] excludeDirs, @SortType int sortType) {
|
||||||
|
ArrayList<File> dirList = new ArrayList<File>();
|
||||||
|
File startDir = new File(startDirPath);
|
||||||
|
if (!startDir.isDirectory()) {
|
||||||
|
return new File[0];
|
||||||
|
}
|
||||||
|
File[] dirs = startDir.listFiles(new FileFilter() {
|
||||||
|
public boolean accept(File f) {
|
||||||
|
if (f == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//noinspection RedundantIfStatement
|
||||||
|
if (f.isDirectory()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (dirs == null) {
|
||||||
|
return new File[0];
|
||||||
|
}
|
||||||
|
if (excludeDirs == null) {
|
||||||
|
excludeDirs = new String[0];
|
||||||
|
}
|
||||||
|
for (File dir : dirs) {
|
||||||
|
File file = dir.getAbsoluteFile();
|
||||||
|
if (!ConvertUtils.toString(excludeDirs).contains(file.getName())) {
|
||||||
|
dirList.add(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sortType == BY_NAME_ASC) {
|
||||||
|
Collections.sort(dirList, new SortByName());
|
||||||
|
} else if (sortType == BY_NAME_DESC) {
|
||||||
|
Collections.sort(dirList, new SortByName());
|
||||||
|
Collections.reverse(dirList);
|
||||||
|
} else if (sortType == BY_TIME_ASC) {
|
||||||
|
Collections.sort(dirList, new SortByTime());
|
||||||
|
} else if (sortType == BY_TIME_DESC) {
|
||||||
|
Collections.sort(dirList, new SortByTime());
|
||||||
|
Collections.reverse(dirList);
|
||||||
|
} else if (sortType == BY_SIZE_ASC) {
|
||||||
|
Collections.sort(dirList, new SortBySize());
|
||||||
|
} else if (sortType == BY_SIZE_DESC) {
|
||||||
|
Collections.sort(dirList, new SortBySize());
|
||||||
|
Collections.reverse(dirList);
|
||||||
|
} else if (sortType == BY_EXTENSION_ASC) {
|
||||||
|
Collections.sort(dirList, new SortByExtension());
|
||||||
|
} else if (sortType == BY_EXTENSION_DESC) {
|
||||||
|
Collections.sort(dirList, new SortByExtension());
|
||||||
|
Collections.reverse(dirList);
|
||||||
|
}
|
||||||
|
return dirList.toArray(new File[dirList.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出指定目录下的所有子目录
|
||||||
|
*/
|
||||||
|
public static File[] listDirs(String startDirPath, String[] excludeDirs) {
|
||||||
|
return listDirs(startDirPath, excludeDirs, BY_NAME_ASC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出指定目录下的所有子目录
|
||||||
|
*/
|
||||||
|
public static File[] listDirs(String startDirPath) {
|
||||||
|
return listDirs(startDirPath, null, BY_NAME_ASC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出指定目录下的所有子目录及所有文件
|
||||||
|
*/
|
||||||
|
public static File[] listDirsAndFiles(String startDirPath, String[] allowExtensions) {
|
||||||
|
File[] dirs, files, dirsAndFiles;
|
||||||
|
dirs = listDirs(startDirPath);
|
||||||
|
if (allowExtensions == null) {
|
||||||
|
files = listFiles(startDirPath);
|
||||||
|
} else {
|
||||||
|
files = listFiles(startDirPath, allowExtensions);
|
||||||
|
}
|
||||||
|
if (dirs == null || files == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
dirsAndFiles = new File[dirs.length + files.length];
|
||||||
|
System.arraycopy(dirs, 0, dirsAndFiles, 0, dirs.length);
|
||||||
|
System.arraycopy(files, 0, dirsAndFiles, dirs.length, files.length);
|
||||||
|
return dirsAndFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出指定目录下的所有子目录及所有文件
|
||||||
|
*/
|
||||||
|
public static File[] listDirsAndFiles(String startDirPath) {
|
||||||
|
return listDirsAndFiles(startDirPath, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出指定目录下的所有文件
|
||||||
|
*/
|
||||||
|
public static File[] listFiles(String startDirPath, final Pattern filterPattern, @SortType int sortType) {
|
||||||
|
LogUtils.verbose(String.format("list file %s", startDirPath));
|
||||||
|
ArrayList<File> fileList = new ArrayList<File>();
|
||||||
|
File f = new File(startDirPath);
|
||||||
|
if (!f.isDirectory()) {
|
||||||
|
return new File[0];
|
||||||
|
}
|
||||||
|
File[] files = f.listFiles(new FileFilter() {
|
||||||
|
public boolean accept(File f) {
|
||||||
|
if (f == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (f.isDirectory()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//noinspection SimplifiableIfStatement
|
||||||
|
if (filterPattern == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return filterPattern.matcher(f.getName()).find();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (files == null) {
|
||||||
|
return new File[0];
|
||||||
|
}
|
||||||
|
for (File file : files) {
|
||||||
|
fileList.add(file.getAbsoluteFile());
|
||||||
|
}
|
||||||
|
if (sortType == BY_NAME_ASC) {
|
||||||
|
Collections.sort(fileList, new SortByName());
|
||||||
|
} else if (sortType == BY_NAME_DESC) {
|
||||||
|
Collections.sort(fileList, new SortByName());
|
||||||
|
Collections.reverse(fileList);
|
||||||
|
} else if (sortType == BY_TIME_ASC) {
|
||||||
|
Collections.sort(fileList, new SortByTime());
|
||||||
|
} else if (sortType == BY_TIME_DESC) {
|
||||||
|
Collections.sort(fileList, new SortByTime());
|
||||||
|
Collections.reverse(fileList);
|
||||||
|
} else if (sortType == BY_SIZE_ASC) {
|
||||||
|
Collections.sort(fileList, new SortBySize());
|
||||||
|
} else if (sortType == BY_SIZE_DESC) {
|
||||||
|
Collections.sort(fileList, new SortBySize());
|
||||||
|
Collections.reverse(fileList);
|
||||||
|
} else if (sortType == BY_EXTENSION_ASC) {
|
||||||
|
Collections.sort(fileList, new SortByExtension());
|
||||||
|
} else if (sortType == BY_EXTENSION_DESC) {
|
||||||
|
Collections.sort(fileList, new SortByExtension());
|
||||||
|
Collections.reverse(fileList);
|
||||||
|
}
|
||||||
|
return fileList.toArray(new File[fileList.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出指定目录下的所有文件
|
||||||
|
*/
|
||||||
|
public static File[] listFiles(String startDirPath, Pattern filterPattern) {
|
||||||
|
return listFiles(startDirPath, filterPattern, BY_NAME_ASC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出指定目录下的所有文件
|
||||||
|
*/
|
||||||
|
public static File[] listFiles(String startDirPath) {
|
||||||
|
return listFiles(startDirPath, null, BY_NAME_ASC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出指定目录下的所有文件
|
||||||
|
*/
|
||||||
|
public static File[] listFiles(String startDirPath, final String[] allowExtensions) {
|
||||||
|
LogUtils.verbose(String.format("list file %s", startDirPath));
|
||||||
|
File file = new File(startDirPath);
|
||||||
|
return file.listFiles(new FilenameFilter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name) {
|
||||||
|
//返回当前目录所有以某些扩展名结尾的文件
|
||||||
|
String extension = FileUtils.getExtension(name);
|
||||||
|
return ConvertUtils.toString(allowExtensions).contains(extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出指定目录下的所有文件
|
||||||
|
*/
|
||||||
|
public static File[] listFiles(String startDirPath, String allowExtension) {
|
||||||
|
return listFiles(startDirPath, new String[]{allowExtension});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断文件或目录是否存在
|
||||||
|
*/
|
||||||
|
public static boolean exist(String path) {
|
||||||
|
File file = new File(path);
|
||||||
|
return file.exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文件或目录
|
||||||
|
*/
|
||||||
|
public static boolean delete(File file, boolean deleteRootDir) {
|
||||||
|
LogUtils.verbose(String.format("delete file %s", file.getAbsolutePath()));
|
||||||
|
boolean result = false;
|
||||||
|
if (file.isFile()) {
|
||||||
|
//是文件
|
||||||
|
result = deleteResolveEBUSY(file);
|
||||||
|
} else {
|
||||||
|
//是目录
|
||||||
|
File[] files = file.listFiles();
|
||||||
|
if (files == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (files.length == 0) {
|
||||||
|
result = deleteRootDir && deleteResolveEBUSY(file);
|
||||||
|
} else {
|
||||||
|
for (File f : files) {
|
||||||
|
delete(f, deleteRootDir);
|
||||||
|
result = deleteResolveEBUSY(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (deleteRootDir) {
|
||||||
|
result = deleteResolveEBUSY(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bug: open failed: EBUSY (Device or resource busy)
|
||||||
|
* fix: http://stackoverflow.com/questions/11539657/open-failed-ebusy-device-or-resource-busy
|
||||||
|
*/
|
||||||
|
private static boolean deleteResolveEBUSY(File file) {
|
||||||
|
// Before you delete a Directory or File: rename it!
|
||||||
|
final File to = new File(file.getAbsolutePath() + System.currentTimeMillis());
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
file.renameTo(to);
|
||||||
|
return to.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文件或目录
|
||||||
|
*/
|
||||||
|
public static boolean delete(String path, boolean deleteRootDir) {
|
||||||
|
File file = new File(path);
|
||||||
|
//noinspection SimplifiableIfStatement
|
||||||
|
if (file.exists()) {
|
||||||
|
return delete(file, deleteRootDir);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文件或目录, 不删除最顶层目录
|
||||||
|
*/
|
||||||
|
public static boolean delete(String path) {
|
||||||
|
return delete(path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文件或目录, 不删除最顶层目录
|
||||||
|
*/
|
||||||
|
public static boolean delete(File file) {
|
||||||
|
return delete(file, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制文件为另一个文件,或复制某目录下的所有文件及目录到另一个目录下
|
||||||
|
*/
|
||||||
|
public static boolean copy(String src, String tar) {
|
||||||
|
File srcFile = new File(src);
|
||||||
|
return srcFile.exists() && copy(srcFile, new File(tar));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制文件或目录
|
||||||
|
*/
|
||||||
|
public static boolean copy(File src, File tar) {
|
||||||
|
try {
|
||||||
|
LogUtils.verbose(String.format("copy %s to %s", src.getAbsolutePath(), tar.getAbsolutePath()));
|
||||||
|
if (src.isFile()) {
|
||||||
|
InputStream is = new FileInputStream(src);
|
||||||
|
OutputStream op = new FileOutputStream(tar);
|
||||||
|
BufferedInputStream bis = new BufferedInputStream(is);
|
||||||
|
BufferedOutputStream bos = new BufferedOutputStream(op);
|
||||||
|
byte[] bt = new byte[1024 * 8];
|
||||||
|
while (true) {
|
||||||
|
int len = bis.read(bt);
|
||||||
|
if (len == -1) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
bos.write(bt, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bis.close();
|
||||||
|
bos.close();
|
||||||
|
} else if (src.isDirectory()) {
|
||||||
|
File[] files = src.listFiles();
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
tar.mkdirs();
|
||||||
|
for (File file : files) {
|
||||||
|
copy(file.getAbsoluteFile(), new File(tar.getAbsoluteFile(), file.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动文件或目录
|
||||||
|
*/
|
||||||
|
public static boolean move(String src, String tar) {
|
||||||
|
return move(new File(src), new File(tar));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动文件或目录
|
||||||
|
*/
|
||||||
|
public static boolean move(File src, File tar) {
|
||||||
|
return rename(src, tar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件重命名
|
||||||
|
*/
|
||||||
|
public static boolean rename(String oldPath, String newPath) {
|
||||||
|
return rename(new File(oldPath), new File(newPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件重命名
|
||||||
|
*/
|
||||||
|
public static boolean rename(File src, File tar) {
|
||||||
|
LogUtils.verbose(String.format("rename %s to %s", src.getAbsolutePath(), tar.getAbsolutePath()));
|
||||||
|
return src.renameTo(tar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取文本文件, 失败将返回空串
|
||||||
|
*/
|
||||||
|
public static String readText(String filepath, String charset) {
|
||||||
|
LogUtils.verbose(String.format("read %s use %s", filepath, charset));
|
||||||
|
try {
|
||||||
|
byte[] data = readBytes(filepath);
|
||||||
|
if (data != null) {
|
||||||
|
return new String(data, charset).trim();
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
LogUtils.warn(e);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取文本文件, 失败将返回空串
|
||||||
|
*/
|
||||||
|
public static String readText(String filepath) {
|
||||||
|
return readText(filepath, "utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取文件内容, 失败将返回空串
|
||||||
|
*/
|
||||||
|
public static byte[] readBytes(String filepath) {
|
||||||
|
LogUtils.verbose(String.format("read %s", filepath));
|
||||||
|
FileInputStream fis = null;
|
||||||
|
try {
|
||||||
|
fis = new FileInputStream(filepath);
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
while (true) {
|
||||||
|
int len = fis.read(buffer, 0, buffer.length);
|
||||||
|
if (len == -1) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
baos.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte[] data = baos.toByteArray();
|
||||||
|
baos.close();
|
||||||
|
return data;
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.warn(e);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
closeSilently(fis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存文本内容
|
||||||
|
*/
|
||||||
|
public static boolean writeText(String filepath, String content, String charset) {
|
||||||
|
try {
|
||||||
|
writeBytes(filepath, content.getBytes(charset));
|
||||||
|
return true;
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
LogUtils.warn(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存文本内容
|
||||||
|
*/
|
||||||
|
public static boolean writeText(String filepath, String content) {
|
||||||
|
return writeText(filepath, content, "utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存文件内容
|
||||||
|
*/
|
||||||
|
public static boolean writeBytes(String filepath, byte[] data) {
|
||||||
|
LogUtils.verbose(String.format("write %s", filepath));
|
||||||
|
File file = new File(filepath);
|
||||||
|
FileOutputStream fos = null;
|
||||||
|
try {
|
||||||
|
if (!file.exists()) {
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
file.getParentFile().mkdirs();
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
file.createNewFile();
|
||||||
|
}
|
||||||
|
fos = new FileOutputStream(filepath);
|
||||||
|
fos.write(data);
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.warn(e);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
closeSilently(fos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 追加文本内容
|
||||||
|
*/
|
||||||
|
public static boolean appendText(String path, String content) {
|
||||||
|
LogUtils.verbose(String.format("append %s", path));
|
||||||
|
File file = new File(path);
|
||||||
|
FileWriter writer = null;
|
||||||
|
try {
|
||||||
|
if (!file.exists()) {
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
file.createNewFile();
|
||||||
|
}
|
||||||
|
writer = new FileWriter(file, true);
|
||||||
|
writer.write(content);
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtils.warn(e);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
closeSilently(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件大小
|
||||||
|
*/
|
||||||
|
public static long getLength(String path) {
|
||||||
|
File file = new File(path);
|
||||||
|
if (!file.isFile() || !file.exists()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return file.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件或网址的名称(包括后缀)
|
||||||
|
*/
|
||||||
|
public static String getName(String pathOrUrl) {
|
||||||
|
if (pathOrUrl == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int pos = pathOrUrl.lastIndexOf('/');
|
||||||
|
if (0 <= pos) {
|
||||||
|
return pathOrUrl.substring(pos + 1);
|
||||||
|
} else {
|
||||||
|
return String.valueOf(System.currentTimeMillis()) + "." + getExtension(pathOrUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件名(不包括扩展名)
|
||||||
|
*/
|
||||||
|
public static String getNameExcludeExtension(String path) {
|
||||||
|
try {
|
||||||
|
String fileName = (new File(path)).getName();
|
||||||
|
int lastIndexOf = fileName.lastIndexOf(".");
|
||||||
|
if (lastIndexOf != -1) {
|
||||||
|
fileName = fileName.substring(0, lastIndexOf);
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.warn(e);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取格式化后的文件大小
|
||||||
|
*/
|
||||||
|
public static String getSize(String path) {
|
||||||
|
long fileSize = getLength(path);
|
||||||
|
return ConvertUtils.toFileSizeString(fileSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件后缀,不包括“.”
|
||||||
|
*/
|
||||||
|
public static String getExtension(String pathOrUrl) {
|
||||||
|
int dotPos = pathOrUrl.lastIndexOf('.');
|
||||||
|
if (0 <= dotPos) {
|
||||||
|
return pathOrUrl.substring(dotPos + 1);
|
||||||
|
} else {
|
||||||
|
return "ext";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取文件的MIME类型
|
||||||
|
*/
|
||||||
|
public static String getMimeType(String pathOrUrl) {
|
||||||
|
String ext = getExtension(pathOrUrl);
|
||||||
|
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||||
|
String mimeType;
|
||||||
|
if (map.hasExtension(ext)) {
|
||||||
|
mimeType = map.getMimeTypeFromExtension(ext);
|
||||||
|
} else {
|
||||||
|
mimeType = "*/*";
|
||||||
|
}
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取格式化后的文件/目录创建或最后修改时间
|
||||||
|
*/
|
||||||
|
public static String getDateTime(String path) {
|
||||||
|
return getDateTime(path, "yyyy-MM-dd HH:mm");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取格式化后的文件/目录创建或最后修改时间
|
||||||
|
*/
|
||||||
|
public static String getDateTime(String path, String format) {
|
||||||
|
File file = new File(path);
|
||||||
|
return getDateTime(file, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取格式化后的文件/目录创建或最后修改时间
|
||||||
|
*/
|
||||||
|
public static String getDateTime(File file, String format) {
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTimeInMillis(file.lastModified());
|
||||||
|
return DateUtils.formatDate(cal.getTime(), format);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 比较两个文件的最后修改时间
|
||||||
|
*/
|
||||||
|
public static int compareLastModified(String path1, String path2) {
|
||||||
|
long stamp1 = (new File(path1)).lastModified();
|
||||||
|
long stamp2 = (new File(path2)).lastModified();
|
||||||
|
if (stamp1 > stamp2) {
|
||||||
|
return 1;
|
||||||
|
} else if (stamp1 < stamp2) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建多级别的目录
|
||||||
|
*/
|
||||||
|
public static boolean makeDirs(String path) {
|
||||||
|
return makeDirs(new File(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建多级别的目录
|
||||||
|
*/
|
||||||
|
public static boolean makeDirs(File file) {
|
||||||
|
return file.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SortByExtension implements Comparator<File> {
|
||||||
|
|
||||||
|
public SortByExtension() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(File f1, File f2) {
|
||||||
|
if (f1 == null || f2 == null) {
|
||||||
|
if (f1 == null) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (f1.isDirectory() && f2.isFile()) {
|
||||||
|
return -1;
|
||||||
|
} else if (f1.isFile() && f2.isDirectory()) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return f1.getName().compareToIgnoreCase(f2.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SortByName implements Comparator<File> {
|
||||||
|
private boolean caseSensitive;
|
||||||
|
|
||||||
|
public SortByName(boolean caseSensitive) {
|
||||||
|
this.caseSensitive = caseSensitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SortByName() {
|
||||||
|
this.caseSensitive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(File f1, File f2) {
|
||||||
|
if (f1 == null || f2 == null) {
|
||||||
|
if (f1 == null) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (f1.isDirectory() && f2.isFile()) {
|
||||||
|
return -1;
|
||||||
|
} else if (f1.isFile() && f2.isDirectory()) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
String s1 = f1.getName();
|
||||||
|
String s2 = f2.getName();
|
||||||
|
if (caseSensitive) {
|
||||||
|
return s1.compareTo(s2);
|
||||||
|
} else {
|
||||||
|
return s1.compareToIgnoreCase(s2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SortBySize implements Comparator<File> {
|
||||||
|
|
||||||
|
public SortBySize() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(File f1, File f2) {
|
||||||
|
if (f1 == null || f2 == null) {
|
||||||
|
if (f1 == null) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (f1.isDirectory() && f2.isFile()) {
|
||||||
|
return -1;
|
||||||
|
} else if (f1.isFile() && f2.isDirectory()) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
if (f1.length() < f2.length()) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SortByTime implements Comparator<File> {
|
||||||
|
|
||||||
|
public SortByTime() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(File f1, File f2) {
|
||||||
|
if (f1 == null || f2 == null) {
|
||||||
|
if (f1 == null) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (f1.isDirectory() && f2.isFile()) {
|
||||||
|
return -1;
|
||||||
|
} else if (f1.isFile() && f2.isDirectory()) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
if (f1.lastModified() > f2.lastModified()) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到所有的存储路径(内部存储+外部存储)
|
||||||
|
*
|
||||||
|
* @param context context
|
||||||
|
* @return 路径列表
|
||||||
|
*/
|
||||||
|
public static List<String> getAllSdPaths(Context context) {
|
||||||
|
Method mMethodGetPaths = null;
|
||||||
|
String[] paths = null;
|
||||||
|
//通过调用类的实例mStorageManager的getClass()获取StorageManager类对应的Class对象
|
||||||
|
//getMethod("getVolumePaths")返回StorageManager类对应的Class对象的getVolumePaths方法,这里不带参数
|
||||||
|
StorageManager mStorageManager = (StorageManager) context
|
||||||
|
.getSystemService(context.STORAGE_SERVICE);//storage
|
||||||
|
try {
|
||||||
|
mMethodGetPaths = mStorageManager.getClass().getMethod("getVolumePaths");
|
||||||
|
paths = (String[]) mMethodGetPaths.invoke(mStorageManager);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (paths != null) {
|
||||||
|
return Arrays.asList(paths);
|
||||||
|
}
|
||||||
|
return new ArrayList<String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用于显示的Sd卡列表
|
||||||
|
* @param sdCardList sdCardList
|
||||||
|
* @return List
|
||||||
|
*/
|
||||||
|
public static List<String> getAllSdCardList(List<String> sdCardList) {
|
||||||
|
List<String> resultList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < sdCardList.size(); i++) {
|
||||||
|
if (i == 0) {
|
||||||
|
//内部存储设备
|
||||||
|
resultList.add("内部存储设备");
|
||||||
|
} else {
|
||||||
|
resultList.add("SD卡"+i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
package com.zlylib.fileselectorlib.utils;
|
||||||
|
|
||||||
|
import android.os.Debug;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.zlylib.fileselectorlib.BuildConfig;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将信息记录到控制台的LogCat,显示调用方法及所在的文件、行号,方便开发时调试查错。
|
||||||
|
* 注意:在Debug状态下开启,在Release状态下关闭,敏感信息不宜打印,否则被非法之徒抓取贻害无穷。
|
||||||
|
*
|
||||||
|
* @author zhangliyang[QQ:1023694760]
|
||||||
|
* @since 2013/11/2
|
||||||
|
*/
|
||||||
|
public final class LogUtils {
|
||||||
|
private static final int MIN_STACK_OFFSET = 3;// starts at this class after two native calls
|
||||||
|
private static final int MAX_STACK_TRACE_SIZE = 131071; //128 KB - 1
|
||||||
|
private static final int METHOD_COUNT = 2; // show method count in trace
|
||||||
|
private static boolean isDebug = BuildConfig.DEBUG;// 是否调试模式
|
||||||
|
private static String debugTag = "FilePicker";// LogCat的标记
|
||||||
|
|
||||||
|
public static void setIsDebug(boolean isDebug) {
|
||||||
|
LogUtils.isDebug = isDebug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDebug() {
|
||||||
|
return isDebug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setDebugTag(String debugTag) {
|
||||||
|
LogUtils.debugTag = debugTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDebugTag() {
|
||||||
|
return debugTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verbose.
|
||||||
|
*
|
||||||
|
* @param message the message
|
||||||
|
*/
|
||||||
|
public static void verbose(String message) {
|
||||||
|
verbose("", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verbose.
|
||||||
|
*
|
||||||
|
* @param object the object
|
||||||
|
* @param message the message
|
||||||
|
*/
|
||||||
|
public static void verbose(Object object, String message) {
|
||||||
|
verbose(object.getClass().getSimpleName(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录“verbose”级别的信息
|
||||||
|
*
|
||||||
|
* @param tag the tag
|
||||||
|
* @param msg the msg
|
||||||
|
*/
|
||||||
|
public static void verbose(String tag, String msg) {
|
||||||
|
if (isDebug) {
|
||||||
|
tag = debugTag + ((tag == null || tag.trim().length() == 0) ? "" : "-") + tag;
|
||||||
|
msg = msg + getTraceElement();
|
||||||
|
Log.v(tag, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug.
|
||||||
|
*
|
||||||
|
* @param message the message
|
||||||
|
*/
|
||||||
|
public static void debug(String message) {
|
||||||
|
debug("", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug.
|
||||||
|
*
|
||||||
|
* @param object the object
|
||||||
|
* @param message the message
|
||||||
|
*/
|
||||||
|
public static void debug(Object object, String message) {
|
||||||
|
debug(object.getClass().getSimpleName(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录“debug”级别的信息
|
||||||
|
*
|
||||||
|
* @param tag the tag
|
||||||
|
* @param msg the msg
|
||||||
|
*/
|
||||||
|
public static void debug(String tag, String msg) {
|
||||||
|
if (isDebug) {
|
||||||
|
tag = debugTag + ((tag == null || tag.trim().length() == 0) ? "" : "-") + tag;
|
||||||
|
msg = msg + getTraceElement();
|
||||||
|
Log.d(tag, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warn.
|
||||||
|
*
|
||||||
|
* @param e the e
|
||||||
|
*/
|
||||||
|
public static void warn(Throwable e) {
|
||||||
|
warn(toStackTraceString(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warn.
|
||||||
|
*
|
||||||
|
* @param message the message
|
||||||
|
*/
|
||||||
|
public static void warn(String message) {
|
||||||
|
warn("", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warn.
|
||||||
|
*
|
||||||
|
* @param object the object
|
||||||
|
* @param message the message
|
||||||
|
*/
|
||||||
|
public static void warn(Object object, String message) {
|
||||||
|
warn(object.getClass().getSimpleName(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warn.
|
||||||
|
*
|
||||||
|
* @param object the object
|
||||||
|
* @param e the e
|
||||||
|
*/
|
||||||
|
public static void warn(Object object, Throwable e) {
|
||||||
|
warn(object.getClass().getSimpleName(), toStackTraceString(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录“warn”级别的信息
|
||||||
|
*
|
||||||
|
* @param tag the tag
|
||||||
|
* @param msg the msg
|
||||||
|
*/
|
||||||
|
public static void warn(String tag, String msg) {
|
||||||
|
if (isDebug) {
|
||||||
|
tag = debugTag + ((tag == null || tag.trim().length() == 0) ? "" : "-") + tag;
|
||||||
|
msg = msg + getTraceElement();
|
||||||
|
Log.w(tag, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error.
|
||||||
|
*
|
||||||
|
* @param e the e
|
||||||
|
*/
|
||||||
|
public static void error(Throwable e) {
|
||||||
|
error(toStackTraceString(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error.
|
||||||
|
*
|
||||||
|
* @param message the message
|
||||||
|
*/
|
||||||
|
public static void error(String message) {
|
||||||
|
error("", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error.
|
||||||
|
*
|
||||||
|
* @param object the object
|
||||||
|
* @param message the message
|
||||||
|
*/
|
||||||
|
public static void error(Object object, String message) {
|
||||||
|
error(object.getClass().getSimpleName(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error.
|
||||||
|
*
|
||||||
|
* @param object the object
|
||||||
|
* @param e the e
|
||||||
|
*/
|
||||||
|
public static void error(Object object, Throwable e) {
|
||||||
|
error(object.getClass().getSimpleName(), toStackTraceString(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录“error”级别的信息
|
||||||
|
*
|
||||||
|
* @param tag the tag
|
||||||
|
* @param msg the msg
|
||||||
|
*/
|
||||||
|
public static void error(String tag, String msg) {
|
||||||
|
if (isDebug) {
|
||||||
|
tag = debugTag + ((tag == null || tag.trim().length() == 0) ? "" : "-") + tag;
|
||||||
|
msg = msg + getTraceElement();
|
||||||
|
Log.e(tag, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在某个方法中调用生成.trace文件。然后拿到电脑上用DDMS工具打开分析
|
||||||
|
*
|
||||||
|
* @see #stopMethodTracing()
|
||||||
|
*/
|
||||||
|
public static void startMethodTracing() {
|
||||||
|
if (isDebug) {
|
||||||
|
Debug.startMethodTracing(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + debugTag + ".trace");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop method tracing.
|
||||||
|
*/
|
||||||
|
public static void stopMethodTracing() {
|
||||||
|
if (isDebug) {
|
||||||
|
Debug.stopMethodTracing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To stack trace string string.
|
||||||
|
* <p>
|
||||||
|
* 此方法参见:https://github.com/Ereza/CustomActivityOnCrash
|
||||||
|
*
|
||||||
|
* @param throwable the throwable
|
||||||
|
* @return the string
|
||||||
|
*/
|
||||||
|
public static String toStackTraceString(Throwable throwable) {
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
PrintWriter pw = new PrintWriter(sw);
|
||||||
|
throwable.printStackTrace(pw);
|
||||||
|
String stackTraceString = sw.toString();
|
||||||
|
//Reduce data to 128KB so we don't get a TransactionTooLargeException when sending the intent.
|
||||||
|
//The limit is 1MB on Android but some devices seem to have it lower.
|
||||||
|
//See: http://developer.android.com/reference/android/os/TransactionTooLargeException.html
|
||||||
|
//And: http://stackoverflow.com/questions/11451393/what-to-do-on-transactiontoolargeexception#comment46697371_12809171
|
||||||
|
if (stackTraceString.length() > MAX_STACK_TRACE_SIZE) {
|
||||||
|
String disclaimer = " [stack trace too large]";
|
||||||
|
stackTraceString = stackTraceString.substring(0, MAX_STACK_TRACE_SIZE - disclaimer.length()) + disclaimer;
|
||||||
|
}
|
||||||
|
pw.close();
|
||||||
|
return stackTraceString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可显示调用方法所在的文件行号,在AndroidStudio的logcat处可点击定位。
|
||||||
|
* 此方法参考:https://github.com/orhanobut/logger
|
||||||
|
*/
|
||||||
|
private static String getTraceElement() {
|
||||||
|
try {
|
||||||
|
int methodCount = METHOD_COUNT;
|
||||||
|
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
|
||||||
|
int stackOffset = _getStackOffset(trace);
|
||||||
|
|
||||||
|
//corresponding method count with the current stack may exceeds the stack trace. Trims the count
|
||||||
|
if (methodCount + stackOffset > trace.length) {
|
||||||
|
methodCount = trace.length - stackOffset - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String level = " ";
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
for (int i = methodCount; i > 0; i--) {
|
||||||
|
int stackIndex = i + stackOffset;
|
||||||
|
if (stackIndex >= trace.length) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
builder.append("\n")
|
||||||
|
.append(level)
|
||||||
|
.append(_getSimpleClassName(trace[stackIndex].getClassName()))
|
||||||
|
.append(".")
|
||||||
|
.append(trace[stackIndex].getMethodName())
|
||||||
|
.append(" ")
|
||||||
|
.append("(")
|
||||||
|
.append(trace[stackIndex].getFileName())
|
||||||
|
.append(":")
|
||||||
|
.append(trace[stackIndex].getLineNumber())
|
||||||
|
.append(")");
|
||||||
|
level += " ";
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(debugTag, e);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the starting index of the stack trace, after method calls made by this class.
|
||||||
|
*
|
||||||
|
* @param trace the stack trace
|
||||||
|
* @return the stack offset
|
||||||
|
*/
|
||||||
|
private static int _getStackOffset(StackTraceElement[] trace) {
|
||||||
|
for (int i = MIN_STACK_OFFSET; i < trace.length; i++) {
|
||||||
|
StackTraceElement e = trace[i];
|
||||||
|
String name = e.getClassName();
|
||||||
|
if (!name.equals(LogUtils.class.getName())) {
|
||||||
|
return --i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String _getSimpleClassName(String name) {
|
||||||
|
int lastIndex = name.lastIndexOf(".");
|
||||||
|
return name.substring(lastIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 nohana, Inc.
|
||||||
|
* Copyright 2017 Zhihu Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.zlylib.fileselectorlib.utils;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import androidx.collection.ArraySet;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MIME Type enumeration to restrict selectable media on the selection activity. Matisse only supports images and
|
||||||
|
* videos.
|
||||||
|
* <p>
|
||||||
|
* Good example of mime types Android supports:
|
||||||
|
* https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/MediaFile.java
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public enum MimeType {
|
||||||
|
|
||||||
|
// ============== images ==============
|
||||||
|
JPEG("image/jpeg", arraySetOf(
|
||||||
|
"jpg",
|
||||||
|
"jpeg"
|
||||||
|
)),
|
||||||
|
PNG("image/png", arraySetOf(
|
||||||
|
"png"
|
||||||
|
)),
|
||||||
|
GIF("image/gif", arraySetOf(
|
||||||
|
"gif"
|
||||||
|
)),
|
||||||
|
BMP("image/x-ms-bmp", arraySetOf(
|
||||||
|
"bmp"
|
||||||
|
)),
|
||||||
|
WEBP("image/webp", arraySetOf(
|
||||||
|
"webp"
|
||||||
|
)),
|
||||||
|
|
||||||
|
// ============== videos ==============
|
||||||
|
MPEG("video/mpeg", arraySetOf(
|
||||||
|
"mpeg",
|
||||||
|
"mpg"
|
||||||
|
)),
|
||||||
|
MP4("video/mp4", arraySetOf(
|
||||||
|
"mp4",
|
||||||
|
"m4v"
|
||||||
|
)),
|
||||||
|
QUICKTIME("video/quicktime", arraySetOf(
|
||||||
|
"mov"
|
||||||
|
)),
|
||||||
|
THREEGPP("video/3gpp", arraySetOf(
|
||||||
|
"3gp",
|
||||||
|
"3gpp"
|
||||||
|
)),
|
||||||
|
THREEGPP2("video/3gpp2", arraySetOf(
|
||||||
|
"3g2",
|
||||||
|
"3gpp2"
|
||||||
|
)),
|
||||||
|
MKV("video/x-matroska", arraySetOf(
|
||||||
|
"mkv"
|
||||||
|
)),
|
||||||
|
WEBM("video/webm", arraySetOf(
|
||||||
|
"webm"
|
||||||
|
)),
|
||||||
|
TS("video/mp2ts", arraySetOf(
|
||||||
|
"ts"
|
||||||
|
)),
|
||||||
|
AVI("video/avi", arraySetOf(
|
||||||
|
"avi"
|
||||||
|
));
|
||||||
|
|
||||||
|
private final String mMimeTypeName;
|
||||||
|
private final Set<String> mExtensions;
|
||||||
|
|
||||||
|
MimeType(String mimeTypeName, Set<String> extensions) {
|
||||||
|
mMimeTypeName = mimeTypeName;
|
||||||
|
mExtensions = extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<MimeType> ofAll() {
|
||||||
|
return EnumSet.allOf(MimeType.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<MimeType> of(MimeType type, MimeType... rest) {
|
||||||
|
return EnumSet.of(type, rest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<MimeType> ofImage() {
|
||||||
|
return EnumSet.of(JPEG, PNG, GIF, BMP, WEBP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<MimeType> ofVideo() {
|
||||||
|
return EnumSet.of(MPEG, MP4, QUICKTIME, THREEGPP, THREEGPP2, MKV, WEBM, TS, AVI);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> arraySetOf(String... suffixes) {
|
||||||
|
return new ArraySet<>(Arrays.asList(suffixes));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return mMimeTypeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkType(ContentResolver resolver, Uri uri) {
|
||||||
|
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||||
|
if (uri == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String type = map.getExtensionFromMimeType(resolver.getType(uri));
|
||||||
|
String path = null;
|
||||||
|
// lazy load the path and prevent resolve for multiple times
|
||||||
|
boolean pathParsed = false;
|
||||||
|
for (String extension : mExtensions) {
|
||||||
|
if (extension.equals(type)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!pathParsed) {
|
||||||
|
// we only resolve the path for one time
|
||||||
|
path = PhotoMetadataUtils.getPath(resolver, uri);
|
||||||
|
if (!TextUtils.isEmpty(path)) {
|
||||||
|
path = path.toLowerCase(Locale.US);
|
||||||
|
}
|
||||||
|
pathParsed = true;
|
||||||
|
}
|
||||||
|
if (path != null && path.endsWith(extension)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package com.zlylib.fileselectorlib.utils;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http://stackoverflow.com/a/27271131/4739220
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class PathUtils {
|
||||||
|
/**
|
||||||
|
* Get a file path from a Uri. This will get the the path for Storage Access
|
||||||
|
* Framework Documents, as well as the _data field for the MediaStore and
|
||||||
|
* other file-based ContentProviders.
|
||||||
|
*
|
||||||
|
* @param context The context.
|
||||||
|
* @param uri The Uri to query.
|
||||||
|
* @author paulburke
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||||
|
public static String getPath(final Context context, final Uri uri) {
|
||||||
|
// DocumentProvider
|
||||||
|
if (Platform.hasKitKat() && DocumentsContract.isDocumentUri(context, uri)) {
|
||||||
|
// ExternalStorageProvider
|
||||||
|
if (isExternalStorageDocument(uri)) {
|
||||||
|
final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
final String[] split = docId.split(":");
|
||||||
|
final String type = split[0];
|
||||||
|
|
||||||
|
if ("primary".equalsIgnoreCase(type)) {
|
||||||
|
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO handle non-primary volumes
|
||||||
|
} else if (isDownloadsDocument(uri)) { // DownloadsProvider
|
||||||
|
|
||||||
|
final String id = DocumentsContract.getDocumentId(uri);
|
||||||
|
final Uri contentUri = ContentUris.withAppendedId(
|
||||||
|
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||||
|
|
||||||
|
return getDataColumn(context, contentUri, null, null);
|
||||||
|
} else if (isMediaDocument(uri)) { // MediaProvider
|
||||||
|
final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
final String[] split = docId.split(":");
|
||||||
|
final String type = split[0];
|
||||||
|
|
||||||
|
Uri contentUri = null;
|
||||||
|
if ("image".equals(type)) {
|
||||||
|
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("video".equals(type)) {
|
||||||
|
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("audio".equals(type)) {
|
||||||
|
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String selection = "_id=?";
|
||||||
|
final String[] selectionArgs = new String[]{
|
||||||
|
split[1]
|
||||||
|
};
|
||||||
|
|
||||||
|
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||||
|
}
|
||||||
|
} else if ("content".equalsIgnoreCase(uri.getScheme())) { // MediaStore (and general)
|
||||||
|
return getDataColumn(context, uri, null, null);
|
||||||
|
} else if ("file".equalsIgnoreCase(uri.getScheme())) { // File
|
||||||
|
return uri.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the data column for this Uri. This is useful for
|
||||||
|
* MediaStore Uris, and other file-based ContentProviders.
|
||||||
|
*
|
||||||
|
* @param context The context.
|
||||||
|
* @param uri The Uri to query.
|
||||||
|
* @param selection (Optional) Filter used in the query.
|
||||||
|
* @param selectionArgs (Optional) Selection arguments used in the query.
|
||||||
|
* @return The value of the _data column, which is typically a file path.
|
||||||
|
*/
|
||||||
|
public static String getDataColumn(Context context, Uri uri, String selection,
|
||||||
|
String[] selectionArgs) {
|
||||||
|
|
||||||
|
Cursor cursor = null;
|
||||||
|
final String column = "_data";
|
||||||
|
final String[] projection = {
|
||||||
|
column
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
final int columnIndex = cursor.getColumnIndexOrThrow(column);
|
||||||
|
return cursor.getString(columnIndex);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri The Uri to check.
|
||||||
|
* @return Whether the Uri authority is ExternalStorageProvider.
|
||||||
|
*/
|
||||||
|
public static boolean isExternalStorageDocument(Uri uri) {
|
||||||
|
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri The Uri to check.
|
||||||
|
* @return Whether the Uri authority is DownloadsProvider.
|
||||||
|
*/
|
||||||
|
public static boolean isDownloadsDocument(Uri uri) {
|
||||||
|
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri The Uri to check.
|
||||||
|
* @return Whether the Uri authority is MediaProvider.
|
||||||
|
*/
|
||||||
|
public static boolean isMediaDocument(Uri uri) {
|
||||||
|
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 nohana, Inc.
|
||||||
|
* Copyright 2017 Zhihu Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.zlylib.fileselectorlib.utils;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
public final class PhotoMetadataUtils {
|
||||||
|
private static final String TAG = PhotoMetadataUtils.class.getSimpleName();
|
||||||
|
private static final int MAX_WIDTH = 1600;
|
||||||
|
private static final String SCHEME_CONTENT = "content";
|
||||||
|
|
||||||
|
private PhotoMetadataUtils() {
|
||||||
|
throw new AssertionError("oops! the utility class is about to be instantiated...");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getPixelsCount(ContentResolver resolver, Uri uri) {
|
||||||
|
Point size = getBitmapBound(resolver, uri);
|
||||||
|
return size.x * size.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Point getBitmapBound(ContentResolver resolver, Uri uri) {
|
||||||
|
InputStream is = null;
|
||||||
|
try {
|
||||||
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
|
options.inJustDecodeBounds = true;
|
||||||
|
is = resolver.openInputStream(uri);
|
||||||
|
BitmapFactory.decodeStream(is, null, options);
|
||||||
|
int width = options.outWidth;
|
||||||
|
int height = options.outHeight;
|
||||||
|
return new Point(width, height);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
return new Point(0, 0);
|
||||||
|
} finally {
|
||||||
|
if (is != null) {
|
||||||
|
try {
|
||||||
|
is.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPath(ContentResolver resolver, Uri uri) {
|
||||||
|
if (uri == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SCHEME_CONTENT.equals(uri.getScheme())) {
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
cursor = resolver.query(uri, new String[]{MediaStore.Images.ImageColumns.DATA},
|
||||||
|
null, null, null);
|
||||||
|
if (cursor == null || !cursor.moveToFirst()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA));
|
||||||
|
} finally {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uri.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float getSizeInMB(long sizeInBytes) {
|
||||||
|
return Float.valueOf(new DecimalFormat("0.0").format((float) sizeInBytes / 1024 / 1024));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.zlylib.fileselectorlib.utils;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JoongWon Baik
|
||||||
|
*/
|
||||||
|
public class Platform {
|
||||||
|
public static boolean hasICS() {
|
||||||
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasKitKat() {
|
||||||
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#D3DFDE"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item
|
||||||
|
android:bottom="1px">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#FFFFFF"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
||||||
|
After Width: | Height: | Size: 454 B |
@@ -0,0 +1,54 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/coordinator_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.zlylib.titlebarlib.ActionBarCommon
|
||||||
|
android:id="@+id/abc"
|
||||||
|
style="@style/ActionBar.Common"
|
||||||
|
app:ab_immersion="immersed"
|
||||||
|
app:ab_statusBarVisible="auto"
|
||||||
|
app:abc_leftIconRes="@drawable/ic_back"
|
||||||
|
app:abc_rightTextPaddingLeft="0dp"
|
||||||
|
app:abc_rightTextPaddingRight="15dp"
|
||||||
|
app:abc_rightText="选择"
|
||||||
|
app:abc_titleText="文件选择" />
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="33dp"
|
||||||
|
android:background="@color/Grey200"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imb_select_sdcard"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:rotation="90"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:src="@mipmap/ic_chevron_right_white_24dp"
|
||||||
|
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/breadcrumbs_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:paddingRight="10dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rcv_file_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_bread"
|
||||||
|
android:minWidth="36dp"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:elevation="0dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textColor="@color/Grey700" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/imb_bread"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:src="@mipmap/ic_chevron_right_white_24dp"
|
||||||
|
android:tint="@color/Grey500"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/white">
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_empty"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:text="无数据"
|
||||||
|
android:layout_below="@id/iv_empty"
|
||||||
|
android:textColor="@color/Grey500"
|
||||||
|
android:textSize="18sp"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:id="@+id/iv_empty"
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:background="@mipmap/lfile_emptyimg"/>
|
||||||
|
</RelativeLayout>
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
android:background="#fff"
|
||||||
|
>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_item_file_select_left"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="42dp"
|
||||||
|
android:src="@mipmap/apk" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginLeft="15dp"
|
||||||
|
android:paddingRight="10dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:background="@drawable/bg_bottom_line_gray"
|
||||||
|
>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_item_file_list"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="22dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:text="风控.apk"
|
||||||
|
android:textColor="#333"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_item_file_list_desc"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="22dp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:text="文件: 文件夹:"
|
||||||
|
android:textColor="@android:color/darker_gray"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatCheckBox
|
||||||
|
android:id="@+id/checkbox_item_file_list"
|
||||||
|
android:checked="true"
|
||||||
|
android:enabled="false"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:clickable="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_item_file_select_right"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@mipmap/right"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:background="@drawable/bg_bottom_line_gray"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmallPopupMenu"
|
||||||
|
android:layout_marginLeft="15dp"
|
||||||
|
android:id="@+id/tv_item_select_sdcard"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
/>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/rcv_pop_select_sdcard"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="#fff" />
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/browser_select_count"
|
||||||
|
android:orderInCategory="100"
|
||||||
|
android:title="选中(0)"
|
||||||
|
app:showAsAction="always"/>
|
||||||
|
|
||||||
|
</menu>
|
||||||
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 120 B |
|
After Width: | Height: | Size: 181 B |
|
After Width: | Height: | Size: 144 B |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 993 B |
|
After Width: | Height: | Size: 894 B |
|
After Width: | Height: | Size: 101 B |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="transparent">#00000000</color>
|
||||||
|
<color name="white">#FFFFFF</color>
|
||||||
|
<color name="Grey50">#FAFAFA</color>
|
||||||
|
<color name="Grey100">#F5F5F5</color>
|
||||||
|
<color name="Grey200">#EEEEEE</color>
|
||||||
|
<color name="Grey300">#E0E0E0</color>
|
||||||
|
<color name="Grey400">#bdbdbd</color>
|
||||||
|
<color name="Grey500">#9e9e9e</color>
|
||||||
|
<color name="Grey600">#757575</color>
|
||||||
|
<color name="Grey700">#616161</color>
|
||||||
|
<color name="Grey800">#3D3D3D</color>
|
||||||
|
<color name="Grey900">#212121</color>
|
||||||
|
<color name="black">#000000</color>
|
||||||
|
|
||||||
|
<color name="titlebg">#6200EE</color>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name" translatable="false">zly</string>
|
||||||
|
<string name="folder_desc" translatable="false">文件:%1$s  |  文件夹:%2$s</string>
|
||||||
|
<string name="file_desc" translatable="false">%1$s  |  %2$s</string>
|
||||||
|
<string name="selected_file_count" translatable="false">选中(%1$s/%2$s)</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="ActionBar">
|
||||||
|
<item name="android:layout_width">match_parent</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:background">?attr/colorPrimary</item>
|
||||||
|
<item name="ab_bottomLineHeight">0dp</item>
|
||||||
|
<item name="ab_statusBarColor">@color/transparent</item>
|
||||||
|
<item name="ab_titleBarHeight">48dp</item>
|
||||||
|
</style>
|
||||||
|
<style name="ActionBar.Common">
|
||||||
|
<item name="abc_titleTextColor">@color/white</item>
|
||||||
|
<item name="abc_titleTextSize">18dp</item>
|
||||||
|
<item name="abc_leftIconColor">@color/white</item>
|
||||||
|
<item name="abc_leftIconPadding">13dp</item>
|
||||||
|
<item name="abc_leftIconMarginLeft">3dp</item>
|
||||||
|
<item name="abc_leftTextColor">@color/white</item>
|
||||||
|
<item name="abc_rightIconColor">@color/white</item>
|
||||||
|
<item name="abc_rightIconPadding">13dp</item>
|
||||||
|
<item name="abc_rightIconMarginRight">3dp</item>
|
||||||
|
<item name="abc_rightTextColor">@color/white</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<external-path name="external_files" path="."/>
|
||||||
|
</paths>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.zlylib.fileselectorlib;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
public class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
public void addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,3 +9,4 @@ include ':locallib_quick_android:quick_android_mvp'
|
|||||||
include ':locallib_quick_android:quick_android_qrcode'
|
include ':locallib_quick_android:quick_android_qrcode'
|
||||||
include ':locallib_quick_android:quick_android_rx_permission'
|
include ':locallib_quick_android:quick_android_rx_permission'
|
||||||
include ':locallib_quick_android:quick_android_webview'
|
include ':locallib_quick_android:quick_android_webview'
|
||||||
|
include ':fileselectorlib'
|
||||||