平时进行android开发时候,可能需要使用各种各样的工具类,每次总要去上网搜索,费时费力,因此特此将其整理发出来,小伙伴可自行收藏.
1.LogUtils工具类
/**
* 控制Log开关的工具类
*/
public class LogUtils {
private LogUtils() {}
// 如果想屏蔽所有log,可以设置为0
public static final int LOG_LEVEL = 6;
public static final int VERBOSE = 5;
public static final int DEBUG = 4;
public static final int INFO = 3;
public static final int WARN = 2;
public static final int ERROR = 1;
public static void v(String tag, String msg) {
if (LOG_LEVEL > VERBOSE) {
Log.v(tag, msg);
}
}
public static void d(String tag, String msg) {
if (LOG_LEVEL > DEBUG) {
Log.d(tag, msg);
}
}
public static void i(String tag, String msg) {
if (LOG_LEVEL > INFO) {
Log.i(tag, msg);
}
}
public static void w(String tag, String msg) {
if (LOG_LEVEL > WARN) {
Log.i(tag, msg);
}
}
public static void e(String tag, String msg) {
if (LOG_LEVEL > ERROR) {
Log.e(tag, msg);
}
}
public static void v(String msg) {
if (LOG_LEVEL > VERBOSE) {
Log.v(getCallerName(), msg);
}
}
public static void d(String msg) {
if (LOG_LEVEL > DEBUG) {
Log.d(getCallerName(), msg);
}
}
public static void i(String msg) {
if (LOG_LEVEL > INFO) {
Log.i(getCallerName(), msg);
}
}
public static void w(String msg) {
if (LOG_LEVEL > WARN) {
Log.w(getCallerName(), msg);
}
}
public static void e(String msg) {
if (LOG_LEVEL > ERROR) {
Log.e(getCallerName(), msg);
}
}
/**
* 获取调用者的类名
*/
public static String getCallerName() {
StackTraceElement caller = Thread.currentThread().getStackTrace()[4];
String className = caller.getClassName();// 带有包名信息
className = className.substring(className.lastIndexOf(".") + 1);
return className;
}
/**
* 描述:日志内容多的时候(超过4k)需要打印全时.
*/
public static void showLog(String str) {
str = str.trim();
int index = 0;
int maxLength = 4000;
String finalString;
while (index < str.length()) {
if (str.length() <= index + maxLength) {
finalString = str.substring(index);
} else {
finalString = str.substring(index, maxLength);
}
index += maxLength;
i(getCallerName(), finalString.trim());
}
}
}
进行android 开发的小伙伴都用过Android Studio,比如有时候我打印log输入一个2048长度的short[]数组时候,你会发现,数组的后半部分没有输出,这是为什么呢?因为一个short类型占2个字节,所以2048个short型的数据就占用4k(超出了Log所规定的单个打印上限),自动被忽略了,当然这种情况毕竟比较少见,大家了解一下.
2.通用的自定义toast工具类
说它通用是因为满足以下需求:
(1).能自定义toast的弹出样式(包括设置toast弹出时的标题和背景等)
(2).既能在主线程中又能在子线程中使用
直接点击链接查看:Android通用自定义toast工具类(可在主线程和子线程中使用)
3.单位转换的辅助类DensityUtil
/**
* 常用单位转换的辅助类
*/
public class DensityUtil {
private DensityUtil() {
throw new UnsupportedOperationException("cannot be instantiated");
}
/**
* dp转px
*/
public static int dp2px(Context context, float dpVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, context.getResources().getDisplayMetrics());
}
/**
* sp转px
*/
public static int sp2px(Context context, float spVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, context.getResources().getDisplayMetrics());
}
/**
* px转dp
*/
public static float px2dp(Context context, float pxVal) {
final float scale = context.getResources().getDisplayMetrics().density;
return (pxVal / scale);
}
/**
* px转sp
*/
public static float px2sp(Context context, float pxVal) {
return (pxVal / context.getResources().getDisplayMetrics().scaledDensity);
}
}
2.史上最全FileUtil工具类
/**
* 1、文件的新建、删除;
* 2、文件的复制;
* 3、获取文件扩展名;
* 4、文件的重命名;
* 5、获取某个文件的详细信息;
* 6、计算某个文件的大小;
* 7、文件大小的格式化;
* 8、获取某个目录下的文件列表;
* 9、目录的新建、删除; 11、目录的复制;
* 10、计算某个目录包含的文件数量;
* 11、计算某个目录包含的文件大小;
*/
public class FileUtil {
private static final String TAG = "FileUtil";
private static final String[][] MIME_MapTable =
{
// {后缀名, MIME类型}
{".3gp", "video/3gpp"}, {".apk", "application/vnd.android.package-archive"},
{".asf", "video/x-ms-asf"}, {".avi", "video/x-msvideo"},
{".bin", "application/octet-stream"}, {".bmp", "image/bmp"}, {".c", "text/plain"},
{".class", "application/octet-stream"}, {".conf", "text/plain"}, {".cpp", "text/plain"},
{".doc", "application/msword"},
{".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
{".xls", "application/vnd.ms-excel"},
{".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{".exe", "application/octet-stream"}, {".gif", "image/gif"},
{".gtar", "application/x-gtar"}, {".gz", "application/x-gzip"}, {".h", "text/plain"},
{".htm", "text/html"}, {".html", "text/html"}, {".jar", "application/java-archive"},
{".java", "text/plain"}, {".jpeg", "image/jpeg"}, {".jpg", "image/jpeg"},
{".js", "application/x-javascript"}, {".log", "text/plain"}, {".m3u", "audio/x-mpegurl"},
{".m4a", "audio/mp4a-latm"}, {".m4b", "audio/mp4a-latm"}, {".m4p", "audio/mp4a-latm"},
{".m4u", "video/vnd.mpegurl"}, {".m4v", "video/x-m4v"}, {".mov", "video/quicktime"},
{".mp2", "audio/x-mpeg"}, {".mp3", "audio/x-mpeg"}, {".mp4", "video/mp4"},
{".mpc", "application/vnd.mpohun.certificate"}, {".mpe", "video/mpeg"},
{".mpeg", "video/mpeg"}, {".mpg", "video/mpeg"}, {".mpg4", "video/mp4"},
{".mpga", "audio/mpeg"}, {".msg", "application/vnd.ms-outlook"}, {".ogg", "audio/ogg"},
{".pdf", "application/pdf"}, {".png", "image/png"},
{".pps", "application/vnd.ms-powerpoint"}, {".ppt", "application/vnd.ms-powerpoint"},
{".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
{".prop", "text/plain"}, {".rc", "text/plain"}, {".rmvb", "audio/x-pn-realaudio"},
{".rtf", "application/rtf"}, {".sh", "text/plain"}, {".tar", "application/x-tar"},
{".tgz", "application/x-compressed"}, {".txt", "text/plain"}, {".wav", "audio/x-wav"},
{".wma", "audio/x-ms-wma"}, {".wmv", "audio/x-ms-wmv"},
{".wps", "application/vnd.ms-works"}, {".xml", "text/plain"},
{".z", "application/x-compress"}, {".zip", "application/x-zip-compressed"}, {"", "*/*"}
};
/**
* 根据文件后缀名获得对应的MIME类型
*
* @param filePath 文件路径
*/
public static String getMIMEType(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
Log.e(TAG, "getMIMEType: 文件不存在");
return "";
}
if (!file.isFile()) {
Log.e(TAG, "getMIMEType: 当前文件类型是目录");
return "";
}
String type = "*/*";
String fileName = file.getName();
int dotIndex = fileName.lastIndexOf("."); // 获取后缀名前的分隔符"."在fileName中的位置
if (dotIndex < 0) {
return type;
}
String end = fileName.substring(dotIndex, fileName.length()).toLowerCase(Locale.getDefault()); // 获取文件的后缀名
if (end.length() == 0) {
return type;
}
// 在MIME和文件类型的匹配表中找到对应的MIME类型
for (String[] aMIME_MapTable : MIME_MapTable) {
if (end.equals(aMIME_MapTable[0])) {
type = aMIME_MapTable[1];
}
}
return type;
}
/**
* 获取设备的sd根路径
*/
public static String getSDPath() {
File sdDir = null;
String sdPath;
boolean sdCardExist = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);//判断sd卡是否存在
if (sdCardExist) {
sdDir = Environment.getExternalStorageDirectory();
}
assert sdDir != null;
sdPath = sdDir.toString();
Log.w(TAG, "getSDPath:" + sdPath);
return sdPath;
}
/**
* 根据文件名获得文件的扩展名
*
* @param fileName 文件名
* @return 文件扩展名(不带点)
*/
public static String getFileSuffix(String fileName) {
Log.w(TAG, "getFileSuffix: fileName::" + fileName);
int index = fileName.lastIndexOf(".");
return fileName.substring(index + 1, fileName.length());
}
//**************************************************创建文件操作*****************************************************************
/**
* 创建文件
*
* @param dirPath 文件所在目录的目录名,如/java/test/1.txt,要在当前目录下创建一个文件名为1.txt的文件
* 则path为/java/test,fileName为1.txt(也可以封装成直接传递文件的绝对路径)
* @param fileName 文件名
* @return 文件新建成功则返回true
*/
public static boolean createFile(String dirPath, String fileName) {
String filePath = dirPath + File.separator + fileName;
Log.w(TAG, "createFile: filePath::" + filePath + " File.separator ::" + File.separator);
File file = new File(filePath);
File fileParent = file.getParentFile();
if (!fileParent.exists()) {
fileParent.mkdirs();
Log.w(TAG, "createFile: 文件所在目录不存在,创建目录成功");
}
if (file.exists()) {
Log.e(TAG, "新建文件失败:file.exist()=" + file.exists());
return false;
} else {
try {
return file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
/**
* 新建目录
*
* @param dir 目录的绝对路径
* @return 创建成功则返回true
*/
public static boolean createFolder(String dir) {
File file = new File(dir);
return file.mkdir();
}
//*******************************************删除文件操作************************************************************************
/**
* 删除单个文件
*
* @param filePath 要删除的文件路径
* @return 文件删除成功则返回true
*/
public static boolean deleteFile(String filePath) {
File file = new File(filePath);
if (file.exists()) {
boolean isDeleted = file.delete();
Log.w(TAG, file.getName() + "删除结果:" + isDeleted);
return isDeleted;
} else {
Log.w(TAG, "文件删除失败:文件不存在!");
return false;
}
}
/**
* 删除单个文件
*
* @param file 要删除的文件对象
* @return 文件删除成功则返回true
*/
private static boolean deleteFile(File file) {
if (file.exists()) {
boolean isDeleted = file.delete();
Log.w(TAG, file.getName() + "删除结果:" + isDeleted);
return isDeleted;
} else {
Log.w(TAG, "文件删除失败:文件不存在!");
return false;
}
}
/**
* 删除文件夹及其包含的所有文件(会自身循环调用)
*
* @param file 要删除的文件对象
* @return 文件删除成功则返回true
*/
public static boolean deleteFolder(File file) {
boolean flag;
File files[] = file.listFiles();
if (files != null) // 目录下存在文件列表
{
for (File f : files) {
if (f.isFile()) {
// 删除子文件
flag = deleteFile(f);
if (!flag) {
return false;
}
} else {
// 删除子目录
flag = deleteFolder(f);
if (!flag) {
return false;
}
}
}
}
//能成功走到这,说明当前目录下的所有子文件和子目录都已经删除完毕
flag = file.delete();//将此空目录也进行删除
return flag;
}
//*************************************复制/重命名操作********************************************
/**
* 复制文件
*
* @param srcPath 源文件绝对路径
* @param destDir 目标文件所在目录
* @return boolean 复制成功则返回true
*/
public static boolean copyFile(String srcPath, String destDir) {
boolean flag = false;
File srcFile = new File(srcPath); // 源文件
if (!srcFile.exists()) {
// 源文件不存在
Log.w(TAG, "copyFile: 源文件不存在");
return false;
}
// 获取待复制文件的文件名
String fileName = srcPath.substring(srcPath.lastIndexOf(File.separator));
String destPath = destDir + fileName;
if (destPath.equals(srcPath)) {
// 源文件路径和目标文件路径重复
Log.w(TAG, "copyFile: 源文件路径和目标文件路径重复");
return false;
}
File destFile = new File(destPath); // 目标文件
if (destFile.exists() && destFile.isFile()) {
// 该路径下已经有一个同名文件
Log.w(TAG, "copyFile: 目标目录下已有同名文件!");
return false;
}
File destFileDir = new File(destDir);
destFileDir.mkdirs();
try {
FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(destFile);
byte[] buf = new byte[1024];
int c;
while ((c = fis.read(buf)) != -1) {
fos.write(buf, 0, c);
}
fis.close();
fos.close();
flag = true;
} catch (IOException e) {
e.printStackTrace();
}
if (flag) {
Log.w(TAG, "copyFile: 复制文件成功!");
}
return flag;
}
/**
* 复制目录
*
* @param srcDir 源文件夹目录
* @param destDir 目标文件夹所在目录
* @return 复制成功则返回true
*/
public static boolean copyFolder(String srcDir, String destDir) {
Log.w(TAG, "copyFolder: 复制文件夹开始!");
boolean flag = false;
File srcFile = new File(srcDir);
if (!srcFile.exists()) {
// 源文件夹不存在
Log.w(TAG, "copyFolder: 源文件夹不存在");
return false;
}
String dirName = getDirName(srcDir); // 获得待复制的文件夹的名字,比如待复制的文件夹为"E://dir"则获取的名字为"dir"
String destPath = destDir ; // 如果想连同源目录名拷贝则String destPath = destDir + File.separator + dirName
// Util.toast("目标文件夹的完整路径为:" + destPath);
if (destPath.equals(srcDir)) {
Log.w(TAG, "copyFolder: 目标文件夹与源文件夹重复");
return false;
}
File destDirFile = new File(destPath);
if (!destDirFile.exists()) {
destDirFile.mkdirs(); // 如果指定目录不存在则生成目录
}
File[] files = srcFile.listFiles(); // 获取源文件夹下的子文件和子文件夹
Log.i(TAG, "copyFolder: -----files,length::"+files.length+" files::"+files);
if (files.length == 0) {
// 如果源文件夹为空目录则直接设置flag为true,这一步非常隐蔽,debug了很久
flag = true;
} else {
for (File temp : files) {
if (temp.isFile()) {
// 文件
flag = copyFile(temp.getAbsolutePath(), destPath);
} else if (temp.isDirectory()) {
// 文件夹
flag = copyFolder(temp.getAbsolutePath(), destPath);
}
if (!flag) {
break;
}
}
}
if (flag) {
Log.w(TAG, "copyFolder: 复制文件夹成功!");
}
return flag;
}
/**
* 重命名文件
*
* @param oldPath 旧文件的绝对路径
* @param newPath 新文件的绝对路径
* @return 文件重命名成功则返回true
*/
public static boolean renameTo(String oldPath, String newPath) {
if (oldPath.equals(newPath)) {
Log.w(TAG, "文件重命名失败:新旧文件名绝对路径相同!");
return false;
}
File oldFile = new File(oldPath);
File newFile = new File(newPath);
boolean isSuccess = oldFile.renameTo(newFile);
Log.w(TAG, "文件重命名是否成功:" + isSuccess);
return isSuccess;
}
/**
* 重命名文件
*
* @param oldFile 旧文件对象,File类型
* @param newName 新文件的文件名,String类型
* @return 重命名成功则返回true
*/
public static boolean renameTo(File oldFile, String newName) {
File newFile = new File(oldFile.getParentFile() + File.separator + newName);
boolean flag = oldFile.renameTo(newFile);
Log.w(TAG, "文件重命名是否成功:" + flag);
return flag;
}
//*********************************计算文件大小/格式化**************************************************************
/**
* 计算某个文件的大小
*
* @param path 文件的绝对路径
* @return
*/
public static long getFileSize(String path) {
File file = new File(path);
if (file.exists()&&file.isFile()) {
return file.length();
}else{
Log.e(TAG, "getFileSize: error!" );
return -1;
}
}
/**
* 计算某个文件的大小
*
* @param file 文件对象
* @return 文件大小,如果file不是文件,则返回-1
*/
private static long getFileSize(File file) {
if (file.isFile()) {
return file.length();
} else {
return -1;
}
}
/**
* 计算某个目录的大小
*
* @param directory 目录生成的file对象
* @return
*/
public static long getFolderSize(File directory) {
File[] files = directory.listFiles();
if (files != null) {
long size = 0;
for (File f : files) {
if (f.isFile()) {
// 获得子文件的大小
size = size + getFileSize(f);
} else {
// 获得子目录的大小
size = size + getFolderSize(f);
}
}
return size;
}
return -1;
}
/**
* 文件大小的格式化
*
* @param size 文件大小,单位为byte
* @return 文件大小格式化后的文本
*/
public static String formatSize(long size) {
DecimalFormat df = new DecimalFormat("####.00");
if (size < 1024) // 小于1KB
{
return size + "Byte";
} else if (size < 1024 * 1024) // 小于1MB
{
float kSize = size / 1024f;
return df.format(kSize) + "KB";
} else if (size < 1024 * 1024 * 1024) // 小于1GB
{
float mSize = size / 1024f / 1024f;
return df.format(mSize) + "MB";
} else if (size < 1024L * 1024L * 1024L * 1024L) // 小于1TB
{
float gSize = size / 1024f / 1024f / 1024f;
return df.format(gSize) + "GB";
} else {
return "size: error";
}
}
/**
* 格式化文件最后修改时间字符串(针对以时间戳结尾的文件)
*
* @param time
* @return
*/
public static String formatTime(long time) {
Date date = new Date(time);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd,HH:mm:ss", Locale.getDefault());
String formatedTime = sdf.format(date);
return formatedTime;
}
//*********************************************获取文件列表*******************************************
/**
* 获取某个目录下的文件列表(如果存在子目录,则子目录中的文件是否计入)
*
* @param dir 文件目录
* @return 文件列表File[] files
*/
public static File[] getFileList(String dir) {
File file = new File(dir);
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
return files;
} else {
return null;
}
} else {
return null;
}
}
//********************************************文件操作中封装的一些小方法*********************************************
/**
* 获取待复制文件夹的文件夹名
*
* @param dir 待复制的目录名称
* @return String
*/
private static String getDirName(String dir) {
if (dir.endsWith(File.separator)) {
// 如果文件夹路径以"//"结尾,则先去除末尾的"//"
dir = dir.substring(0, dir.lastIndexOf(File.separator));
}
return dir.substring(dir.lastIndexOf(File.separator) + 1);
}
/**
* 计算某个目录包含的文件数量
*
* @param directory
* @return
*/
public static int getFileCount(File directory) {
File[] files = directory.listFiles();
int count = files.length;
return count;
}
}
关于FileUtil工具类我多说点吧。
上述贴出的FileUtil工具类,所有的方法都经过了测试,有些地方还做了优化,鉴于代码不多,我下面将测试代码和资源文件贴出来:
@RuntimePermissions
public class SecondActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "SecondActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_activity);
findViewById(R.id.get_sd_path).setOnClickListener(this);
findViewById(R.id.get_file_suffix).setOnClickListener(this);
findViewById(R.id.get_file_mime).setOnClickListener(this);
findViewById(R.id.create_file).setOnClickListener(this);
findViewById(R.id.create_dir).setOnClickListener(this);
findViewById(R.id.delete_specific_file).setOnClickListener(this);
findViewById(R.id.delete_all_for_dir).setOnClickListener(this);
findViewById(R.id.copy_file).setOnClickListener(this);
findViewById(R.id.copy_dir).setOnClickListener(this);
findViewById(R.id.rename_file).setOnClickListener(this);
findViewById(R.id.format_file_size).setOnClickListener(this);
findViewById(R.id.format_dir_size).setOnClickListener(this);
findViewById(R.id.get_file_list).setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.get_sd_path:
//eg."getSdPath:/storage/emulated/0"
//获取sd路径
SecondActivityPermissionsDispatcher.getSdPathWithCheck(SecondActivity.this);
break;
case R.id.get_file_suffix:
//读取文件后缀
String fileName = "log.txt";
String fileSuffix = FileUtil.getFileSuffix(fileName);
Log.i(TAG, "onClick: fileSuffix::" + fileSuffix);
break;
case R.id.get_file_mime:
//获取文件MIME类型
String filePath1 = FileUtil.getSDPath() + "/00/" + "1.doc";
String mimeType = FileUtil.getMIMEType(filePath1);
Log.i(TAG, "onClick: mimeType::" + mimeType);
break;
case R.id.create_file:
//创建文件
String dirPath2 = FileUtil.getSDPath() + "/00";
String fileName2 = "2.doc";
boolean isCreated = FileUtil.createFile(dirPath2, fileName2);
Log.i(TAG, "onClick: file isCreated::" + isCreated);
break;
case R.id.create_dir:
//创建目录
String dirPath3 = FileUtil.getSDPath()+"/00";
boolean isCreated3 = FileUtil.createFolder(dirPath3);
Log.i(TAG, "onClick: dir isCreated::" + isCreated3);
break;
case R.id.delete_specific_file:
//删除指定的某个文件
String filePath4 = FileUtil.getSDPath() + "/00/4.doc";
boolean isDeleted = FileUtil.deleteFile(filePath4);
Log.i(TAG, "onClick: file isDeleted::" + isDeleted);
break;
case R.id.delete_all_for_dir:
//删除某一目录下的所有文件
String dirPath5 = FileUtil.getSDPath() + "/00";
boolean isDeleted5 = FileUtil.deleteFolder(new File(dirPath5));
Log.i(TAG, "onClick: all sub file isDeleted(include current dir)::" + isDeleted5);
break;
case R.id.copy_file:
//复制文件
String filePath6 = FileUtil.getSDPath() + "/00/6.wav";
String dirPath6 = FileUtil.getSDPath() + "/11";
boolean isCopyed = FileUtil.copyFile(filePath6, dirPath6);
Log.i(TAG, "onClick: file isCopyed::" + isCopyed);
break;
case R.id.copy_dir://v
//复制目录
String srcDir7 = FileUtil.getSDPath() + "/00";
String destDir7 = FileUtil.getSDPath() + "/11";
boolean isCopyed7 = FileUtil.copyFolder(srcDir7, destDir7);
Log.i(TAG, "onClick: file isCopyed::" + isCopyed7);
break;
case R.id.rename_file:
//文件重命名
String srcPath8 = FileUtil.getSDPath() + "/00/8.wav";
String destPath8 = FileUtil.getSDPath() + "/00/8_1.wav";
boolean b = FileUtil.renameTo(srcPath8, destPath8);
//filename="7_1.wav";
//boolean b = FileUtil.renameTo(new File(srcPath8), destPath8);
Log.i(TAG, "onClick: file rename::" + b);
break;
case R.id.format_file_size://593.71kb---->579.79
//格式化文件大小
String filePath9 = FileUtil.getSDPath() + "/00/9.wav";
long fileSize = FileUtil.getFileSize(filePath9);
Log.i(TAG, "onClick: get fileSize::" + fileSize);
if (fileSize != -1) {
String formatSize = FileUtil.formatSize(fileSize);
Log.i(TAG, "onClick:formatSize file::" + formatSize);
}
break;
case R.id.format_dir_size://4.95--->4.72M
//格式化目录大小
String dir10 = FileUtil.getSDPath() + "/11";
long folderSize = FileUtil.getFolderSize(new File(dir10));
if (folderSize != -1) {
String formatSize10 = FileUtil.formatSize(folderSize);
Log.i(TAG, "onClick: formatSize dir::" + formatSize10);
}
break;
case R.id.get_file_list:
//获取当前目录的文件列表
String dir11 = FileUtil.getSDPath() + "/11";
File[] fileList = FileUtil.getFileList(dir11);
Log.i(TAG, "onClick: get file list size::" + fileList.length + " fileList::" + fileList);
break;
}
}
//*****************************封装的文件操作方法***********************************************
@NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
void getSdPath() {
String sdPath = FileUtil.getSDPath();
Log.d(TAG, "getSdPath:"+sdPath);
}
//****************************************************************************
//首次权限弹出是系统权限。当点击拒绝之后才会走此
// 向用户说明为什么需要这些权限(可选)
@OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)
void showRationaleForCamera(final PermissionRequest request) {
new AlertDialog.Builder(this)
.setMessage("文件操作需要读写权限,请求授予")
.setPositiveButton("下一步", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int button) {
request.proceed();//继续请求权限
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int button) {
request.cancel();
}
})
.show();
}
// 用户拒绝授权回调(可选)//当点击系统权限的拒绝时候,不会触发
@OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE)
void showDeniedForCamera() {
Toast.makeText(this, "读写权限被拒绝。", Toast.LENGTH_SHORT).show();
}
// 用户勾选了“不再提醒”时调用(可选)
@OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE)
void showNeverAskForCamera() {
openAppDetails();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 代理权限处理到自动生成的方法
//必须手动添加,不然下一步不走。。
SecondActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
}
/**
* 打开 APP 的详情设置
*/
private void openAppDetails() {
Toast.makeText(this, "勾选了不在拒绝。", Toast.LENGTH_SHORT).show();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("操作文件读写权限,请到'设置'中授予权限.");
builder.setPositiveButton("去手动授权", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse("package:" + getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
}
});
builder.setNegativeButton("取消", null);
builder.show();
}
}
布局文件second_activity.xml:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/get_sd_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="获取sd卡根路径" />
<Button
android:id="@+id/get_file_suffix"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="获取文件后缀名" />
<Button
android:id="@+id/get_file_mime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="获取文件MIME类型" />
<Button
android:id="@+id/create_file"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="创建文件" />
<Button
android:id="@+id/create_dir"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="创建目录" />
<Button
android:id="@+id/delete_specific_file"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="删除指定文件(单个)" />
<Button
android:id="@+id/delete_all_for_dir"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="删除某一目录下的所有文件(包括此目录)" />
<Button
android:id="@+id/copy_file"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="复制文件" />
<Button
android:id="@+id/copy_dir"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="复制目录" />
<Button
android:id="@+id/rename_file"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="文件重命名" />
<Button
android:id="@+id/format_file_size"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="计算某个文件大小,并格式化" />
<Button
android:id="@+id/format_dir_size"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="计算某个目录大小,并格式化" />
<Button
android:id="@+id/get_file_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="获取某个路径下的文件列表" />
</LinearLayout>
</ScrollView>
运行效果如下:
其中SecondActivity中设计android 6.0运行时权限的申请,使用教程可参考:
PermissionsDispatcher,Android 6.0 运行时权限
重点说明,避免踩坑:
(1).@NeedsPermission修饰的方法只能被void修饰
(2).android6.0动态权限申请不意味着只在代码中申请,清单文件也必须写。
(3).@NeedsPermission可以使用多次,即需要权限申请的都可以使用,不受次数限制
(4).FileUtils中的计算文件大小在小米5手机测试时候存在一定的偏差,比如某个文大小工具类中计算出来的是579.79KB,而通过小米手机的文件管理器查出来的是593.71KB. 这主要是进制的原因,MIUI目前使用的是1000进制,其他的app可能采用的是1024进制,因为存储芯片的生产厂商都是按照1000进制计算的这个问题的原因参见
3.常用的正则表达式验证
/**
* 常用的正则表达式
*/
public class RegularUtils {
/**
* 验证手机号(简单)
*/
private static final String REGEX_MOBILE_SIMPLE = "^[1]\\d{10}$";
/**
* 验证手机号(精确)
* 移动:134(0-8)、135、136、137、138、139、147、150、151、152、157、158、159、178、182、183、184、187、188
* 联通:130、131、132、145、155、156、175、176、185、186
* 电信:133、153、173、177、180、181、189
* 全球星:1349
* 虚拟运营商:170
*/
private static final String REGEX_MOBILE_EXACT = "^((13[0-9])|(14[5,7])|(15[0-3,5-8])|(17[0,3,5-8])|(18[0-9])|(147))\\d{8}$";
/**
* 验证座机号,正确格式:xxx/xxxx-xxxxxxx/xxxxxxxx/
*/
private static final String REGEX_TEL = "^0\\d{2,3}[- ]?\\d{7,8}";
/**
* 验证邮箱
*/
private static final String REGEX_EMAIL = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";
/**
* 验证url
*/
private static final String REGEX_URL = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?";
/**
* 验证汉字
*/
private static final String REGEX_CHZ = "^[\\u4e00-\\u9fa5]+$";
/**
* 验证用户名,取值范围为a-z,A-Z,0-9,"_",汉字,不能以"_"结尾,用户名必须是6-20位
*/
private static final String REGEX_USERNAME = "^[\\w\\u4e00-\\u9fa5]{6,20}(?<!_)$";
/**
* 验证IP地址
*/
private static final String REGEX_IP = "((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)";
//If u want more please visit http://toutiao.com/i6231678548520731137/
/**
* * @param string 待验证文本
*
* @return 是否符合手机号(简单)格式
*/
public static boolean isMobileSimple(String string) {
return isMatch(REGEX_MOBILE_SIMPLE, string);
}
/**
* @param string 待验证文本
* @return 是否符合手机号(精确)格式
*/
public static boolean isMobileExact(String string) {
return isMatch(REGEX_MOBILE_EXACT, string);
}
/**
* @param string 待验证文本
* @return 是否符合座机号码格式
*/
public static boolean isTel(String string) {
return isMatch(REGEX_TEL, string);
}
/**
* @param string 待验证文本
* @return 是否符合邮箱格式
*/
public static boolean isEmail(String string) {
return isMatch(REGEX_EMAIL, string);
}
/**
* @param string 待验证文本
* @return 是否符合网址格式
*/
public static boolean isURL(String string) {
return isMatch(REGEX_URL, string);
}
/**
* @param string 待验证文本
* @return 是否符合汉字
*/
public static boolean isChz(String string) {
return isMatch(REGEX_CHZ, string);
}
/**
* @param string 待验证文本
* @return 是否符合用户名
*/
public static boolean isUsername(String string) {
return isMatch(REGEX_USERNAME, string);
}
/**
* @param regex 正则表达式字符串
* @param string 要匹配的字符串
* @return 如果str 符合 regex的正则表达式格式,返回true, 否则返回 false;
*/
public static boolean isMatch(String regex, String string) {
return !TextUtils.isEmpty(string) && Pattern.matches(regex, string);
}
}
4.时间戳工具类
/**
* 时间戳
*/
public class DateUtils {
private static SimpleDateFormat sf;
private static SimpleDateFormat sdf;
/**
* 获取系统时间 格式为:"yyyy/MM/dd "
**/
public static String getCurrentDate() {
Date d = new Date();
sf = new SimpleDateFormat("yyyy年MM月dd日");
return sf.format(d);
}
/**
* 获取系统时间 格式为:"yyyy "
**/
public static String getCurrentYear() {
Date d = new Date();
sf = new SimpleDateFormat("yyyy");
return sf.format(d);
}
/**
* 获取系统时间 格式为:"MM"
**/
public static String getCurrentMonth() {
Date d = new Date();
sf = new SimpleDateFormat("MM");
return sf.format(d);
}
/**
* 获取系统时间 格式为:"dd"
**/
public static String getCurrentDay() {
Date d = new Date();
sf = new SimpleDateFormat("dd");
return sf.format(d);
}
/**
* 获取当前时间戳 * * @return
*/
public static long getCurrentTime() {
long d = new Date().getTime() / 1000;
return d;
}
/**
* 时间戳转换成字符窜
*/
public static String getDateToString(long time) {
Date d = new Date(time * 1000);
sf = new SimpleDateFormat("yyyy年MM月dd日");
return sf.format(d);
}
/**
* 时间戳中获取年
*/
public static String getYearFromTime(long time) {
Date d = new Date(time * 1000);
sf = new SimpleDateFormat("yyyy");
return sf.format(d);
}
/**
* 时间戳中获取月
*/
public static String getMonthFromTime(long time) {
Date d = new Date(time * 1000);
sf = new SimpleDateFormat("MM");
return sf.format(d);
}
/**
* 时间戳中获取日
*/
public static String getDayFromTime(long time) {
Date d = new Date(time * 1000);
sf = new SimpleDateFormat("dd");
return sf.format(d);
}
/**
* 将字符串转为时间戳
*/
public static long getStringToDate(String time) {
sdf = new SimpleDateFormat("yyyy年MM月dd日");
Date date = new Date();
try {
date = sdf.parse(time);
} catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace();
}
return date.getTime();
}
/**
* 2018-12-13T13:33:43.441+08:00--->2018-12-13 13:33:43
*
* @param str
* @return
*/
public static String getGeneralTime(String str) {
if (TextUtils.isEmpty(str)) {
return "";
}
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date date = null;
try {
date = formatter.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
/**
* get the one day of past
*
* @param past
* @return
*/
public static String getPastDate(int past) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.DAY_OF_YEAR, calendar.get(Calendar.DAY_OF_YEAR) - past);
Date today = calendar.getTime();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
String result = format.format(today);
Log.e(null, result);
return result;
}
/**
* 比较两个日期的大小,日期格式为yyyy-MM-dd
*
* @param startDateStr the first date
* @param endDateStr the second date
* @return true <br/>false
*/
public static boolean isDate2Bigger(String startDateStr, String endDateStr) {
boolean isBigger = false;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date dt1 = null;
Date dt2 = null;
try {
dt1 = sdf.parse(startDateStr);
dt2 = sdf.parse(endDateStr);
} catch (ParseException e) {
e.printStackTrace();
}
if (dt1.getTime() > dt2.getTime()) {
isBigger = false;
} else if (dt1.getTime() <= dt2.getTime()) {
isBigger = true;
}
return isBigger;
}
/**
* 比较两个日期的大小间隔是否超过30天,日期格式为yyyy-MM-dd
*
* @param startDateStr the start date
* @param endDateStr the end date
* @return true <br/>false
*/
public static boolean isDateOverOneMonth(String startDateStr, String endDateStr) {
boolean isOverOneMonth = false;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date dt1 = null;
Date dt2 = null;
try {
dt1 = sdf.parse(startDateStr);
dt2 = sdf.parse(endDateStr);
} catch (ParseException e) {
e.printStackTrace();
}
long dt1Time = dt1.getTime();
long dt2Time = dt2.getTime();
int period = (int) ((dt2Time - dt1Time) / (24 * 60 * 60 * 1000));
if (period > 30) {
isOverOneMonth = true;
}
return isOverOneMonth;
}
/**
* 比较两个日期之间间隔的天数
*
* @param startDateStr
* @param endDateStr
* @return
*/
public static int getIntervalDays(String startDateStr, String endDateStr, SimpleDateFormat sdf) {
int intervalDays;
Date dt1 = null;
Date dt2 = null;
try {
dt1 = sdf.parse(startDateStr);
dt2 = sdf.parse(endDateStr);
} catch (ParseException e) {
e.printStackTrace();
}
long dt1Time = dt1.getTime();
long dt2Time = dt2.getTime();
intervalDays = (int) ((dt2Time - dt1Time) / (24 * 60 * 60 * 1000));
return intervalDays;
}
/**
* 获取两个日期之间的所有日期
*
* @param startTime 开始日期
* @param endTime 结束日期
* @return
*/
public static List<String> getIntervalDays(String startTime, String endTime) {
// 返回的日期集合
List<String> days = new ArrayList<>();
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
Date start = dateFormat.parse(startTime);
Date end = dateFormat.parse(endTime);
Calendar tempStart = Calendar.getInstance();
tempStart.setTime(start);
Calendar tempEnd = Calendar.getInstance();
tempEnd.setTime(end);
tempEnd.add(Calendar.DATE, +1);// 日期加1(包含结束)
while (tempStart.before(tempEnd)) {
days.add(dateFormat.format(tempStart.getTime()));
tempStart.add(Calendar.DAY_OF_YEAR, 1);
}
} catch (ParseException e) {
e.printStackTrace();
}
return days;
}
/**
* 比较两个日期之间的间隔
*
* @param startDateStr
* @param endDateStr
* @return
*/
public static String getIntervalTime(String startDateStr, String endDateStr, SimpleDateFormat sdf) {
LogUtils.d(TAG, "getIntervalDays, startDateStr:" + startDateStr + " endDateStr:" + endDateStr);
Date dt1 = null;
Date dt2 = null;
try {
dt1 = sdf.parse(startDateStr);
dt2 = sdf.parse(endDateStr);
} catch (ParseException e) {
e.printStackTrace();
}
double dt1Time = dt1.getTime();
double dt2Time = dt2.getTime();
double time = dt2Time - dt1Time;
int intervalMins = (int) (time / (TimeConstants.MIN));
int intervalHours = (int) (time / (TimeConstants.HOUR));
int intervalDays = (int) (time / (TimeConstants.DAY));
int intervalWeeks = (intervalDays / 7);
int intervalMonths = (intervalDays / 30);
int intervalYears = (intervalDays / 365);
LogUtils.d(TAG, "" +
"intervalMins::" + intervalMins + "\n" +
" intervalHours::" + intervalHours + "\n" +
" intervalDays::" + intervalDays + "\n" +
" intervalWeeks::" + intervalWeeks + "\n" +
" intervalMonths::" + intervalMonths + "\n" +
" intervalYears::" + intervalYears + "\n");
if (intervalYears < 1) {//in year
if (intervalMonths < 1) {//in month
if (intervalWeeks < 1) {//in week
if (intervalDays < 1) {//in day
if (intervalHours < 1) {//in hour
if (intervalMins < 10) {//in minute
return "刚刚";
}
return intervalMins + "分钟前";
}
return intervalHours + "小时前";
}
return intervalDays + "天前";
}
return intervalWeeks + "周前";
}
return intervalMonths + "月前";
}
return intervalYears + "年前";
}
/***
* 获取一天之内所有的时间点的集合
* @return
*/
public static List<String> getOneDayTimeList() {
ArrayList<String> timeList = new ArrayList<>();
for (int i = 0; i < 24; i++) {
timeList.add(i + ":00");
}
return timeList;
}
}
4. 6.0+权限申请工具类
/**
* <pre>
* author: Blankj
* blog : http://blankj.com
* time : 2017/12/29
* desc : utils about permission
* </pre>
*/
public final class PermissionUtils {
private static final List<String> PERMISSIONS = getPermissions();
private static PermissionUtils sInstance;
private OnRationaleListener mOnRationaleListener;
private SimpleCallback mSimpleCallback;
private FullCallback mFullCallback;
private ThemeCallback mThemeCallback;
private Set<String> mPermissions;
private List<String> mPermissionsRequest;
private List<String> mPermissionsGranted;
private List<String> mPermissionsDenied;
private List<String> mPermissionsDeniedForever;
/**
* Return the permissions used in application.
*
* @return the permissions used in application
*/
public static List<String> getPermissions() {
return getPermissions(Utils.getApp().getPackageName());
}
/**
* Return the permissions used in application.
*
* @param packageName The name of the package.
* @return the permissions used in application
*/
public static List<String> getPermissions(final String packageName) {
PackageManager pm = Utils.getApp().getPackageManager();
try {
return Arrays.asList(
pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS)
.requestedPermissions
);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return Collections.emptyList();
}
}
/**
* Return whether <em>you</em> have granted the permissions.
*
* @param permissions The permissions.
* @return {@code true}: yes<br>{@code false}: no
*/
public static boolean isGranted(final String... permissions) {
for (String permission : permissions) {
if (!isGranted(permission)) {
return false;
}
}
return true;
}
private static boolean isGranted(final String permission) {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M
|| PackageManager.PERMISSION_GRANTED
== ContextCompat.checkSelfPermission(Utils.getApp(), permission);
}
/**
* Launch the application's details settings.
*/
public static void launchAppDetailsSettings() {
Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.setData(Uri.parse("package:" + Utils.getApp().getPackageName()));
Utils.getApp().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
/**
* Set the permissions.
*
* @param permissions The permissions.
* @return the single {@link PermissionUtils} instance
*/
public static PermissionUtils permission(@PermissionConstants.Permission final String... permissions) {
return new PermissionUtils(permissions);
}
private PermissionUtils(final String... permissions) {
mPermissions = new LinkedHashSet<>();
for (String permission : permissions) {
for (String aPermission : PermissionConstants.getPermissions(permission)) {
if (PERMISSIONS.contains(aPermission)) {
mPermissions.add(aPermission);
}
}
}
sInstance = this;
}
/**
* Set rationale listener.
*
* @param listener The rationale listener.
* @return the single {@link PermissionUtils} instance
*/
public PermissionUtils rationale(final OnRationaleListener listener) {
mOnRationaleListener = listener;
return this;
}
/**
* Set the simple call back.
*
* @param callback the simple call back
* @return the single {@link PermissionUtils} instance
*/
public PermissionUtils callback(final SimpleCallback callback) {
mSimpleCallback = callback;
return this;
}
/**
* Set the full call back.
*
* @param callback the full call back
* @return the single {@link PermissionUtils} instance
*/
public PermissionUtils callback(final FullCallback callback) {
mFullCallback = callback;
return this;
}
/**
* Set the theme callback.
*
* @param callback The theme callback.
* @return the single {@link PermissionUtils} instance
*/
public PermissionUtils theme(final ThemeCallback callback) {
mThemeCallback = callback;
return this;
}
/**
* Start request.
*/
public void request() {
mPermissionsGranted = new ArrayList<>();
mPermissionsRequest = new ArrayList<>();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
mPermissionsGranted.addAll(mPermissions);
requestCallback();
} else {
for (String permission : mPermissions) {
if (isGranted(permission)) {
mPermissionsGranted.add(permission);
} else {
mPermissionsRequest.add(permission);
}
}
if (mPermissionsRequest.isEmpty()) {
requestCallback();
} else {
startPermissionActivity();
}
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
private void startPermissionActivity() {
mPermissionsDenied = new ArrayList<>();
mPermissionsDeniedForever = new ArrayList<>();
PermissionActivity.start(Utils.getApp());
}
@RequiresApi(api = Build.VERSION_CODES.M)
private boolean rationale(final Activity activity) {
boolean isRationale = false;
if (mOnRationaleListener != null) {
for (String permission : mPermissionsRequest) {
if (activity.shouldShowRequestPermissionRationale(permission)) {
getPermissionsStatus(activity);
mOnRationaleListener.rationale(new OnRationaleListener.ShouldRequest() {
@Override
public void again(boolean again) {
if (again) {
startPermissionActivity();
} else {
requestCallback();
}
}
});
isRationale = true;
break;
}
}
mOnRationaleListener = null;
}
return isRationale;
}
private void getPermissionsStatus(final Activity activity) {
for (String permission : mPermissionsRequest) {
if (isGranted(permission)) {
mPermissionsGranted.add(permission);
} else {
mPermissionsDenied.add(permission);
if (!activity.shouldShowRequestPermissionRationale(permission)) {
mPermissionsDeniedForever.add(permission);
}
}
}
}
private void requestCallback() {
if (mSimpleCallback != null) {
if (mPermissionsRequest.size() == 0
|| mPermissions.size() == mPermissionsGranted.size()) {
mSimpleCallback.onGranted();
} else {
if (!mPermissionsDenied.isEmpty()) {
mSimpleCallback.onDenied();
}
}
mSimpleCallback = null;
}
if (mFullCallback != null) {
if (mPermissionsRequest.size() == 0
|| mPermissions.size() == mPermissionsGranted.size()) {
mFullCallback.onGranted(mPermissionsGranted);
} else {
if (!mPermissionsDenied.isEmpty()) {
mFullCallback.onDenied(mPermissionsDeniedForever, mPermissionsDenied);
}
}
mFullCallback = null;
}
mOnRationaleListener = null;
mThemeCallback = null;
}
private void onRequestPermissionsResult(final Activity activity) {
getPermissionsStatus(activity);
requestCallback();
}
@RequiresApi(api = Build.VERSION_CODES.M)
public static class PermissionActivity extends Activity {
public static void start(final Context context) {
Intent starter = new Intent(context, PermissionActivity.class);
starter.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(starter);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
if (sInstance == null) {
super.onCreate(savedInstanceState);
Log.e("PermissionUtils", "request permissions failed");
finish();
return;
}
if (sInstance.mThemeCallback != null) {
sInstance.mThemeCallback.onActivityCreate(this);
}
super.onCreate(savedInstanceState);
if (sInstance.rationale(this)) {
finish();
return;
}
if (sInstance.mPermissionsRequest != null) {
int size = sInstance.mPermissionsRequest.size();
if (size <= 0) {
finish();
return;
}
requestPermissions(sInstance.mPermissionsRequest.toArray(new String[size]), 1);
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
sInstance.onRequestPermissionsResult(this);
finish();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
finish();
return true;
}
}
/**
* 检查是否拥有权限
*/
public static boolean hasPermission(Context context, String perm) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (context.getApplicationContext().checkSelfPermission(perm) != PackageManager.PERMISSION_DENIED) {
return true;
}
return false;
}
return true;
}
/**
* 批量申请权限(如果当前没有权限的话)。授权结果在onRequestPermissionsResult中处理
*/
public static void requestPermissionsIfNeed(Activity activity, String[] perms, int requestCode){
if(perms.length == 0) {
return;
}
HashSet<String> needPerms = new HashSet<>();
for (String perm : perms){
if(!isGranted(perm)){
needPerms.add(perm);
}
}
if(needPerms.size() == 0){
return;
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
activity.requestPermissions(needPerms.toArray(new String[needPerms.size()]), requestCode);
}
}
}
///
// interface
///
public interface OnRationaleListener {
void rationale(ShouldRequest shouldRequest);
interface ShouldRequest {
void again(boolean again);
}
}
public interface SimpleCallback {
void onGranted();
void onDenied();
}
public interface FullCallback {
void onGranted(List<String> permissionsGranted);
void onDenied(List<String> permissionsDeniedForever, List<String> permissionsDenied);
}
public interface ThemeCallback {
void onActivityCreate(Activity activity);
}
}