Files
Aria/PublicComponent/src/main/java/com/arialyy/aria/util/CommonUtil.java
2020-02-06 22:22:20 +08:00

833 lines
24 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
*
* 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.arialyy.aria.util;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Base64;
import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.core.FtpUrlEntity;
import dalvik.system.DexFile;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by lyy on 2016/1/22. 通用工具
*/
public class CommonUtil {
private static final String TAG = "CommonUtil";
public static final String SERVER_CHARSET = "ISO-8859-1";
private static long lastClickTime;
/**
* 获取线程名称命名规则md5(任务地址 + 线程id)
*
* @param url 任务地址
* @param threadId 线程id
*/
public static String getThreadName(String url, int threadId) {
return getStrMd5(url.concat(String.valueOf(threadId)));
}
/**
* 检查sql的expression是否合法
*
* @return false 不合法
*/
public static boolean checkSqlExpression(String... expression) {
if (expression.length == 0) {
ALog.e(TAG, "sql语句表达式不能为null");
return false;
}
//if (expression.length == 1) {
// ALog.e(TAG, String.format("表达式需要写入参数,参数信息:%s", Arrays.toString(expression)));
// return false;
//}
String where = expression[0];
//if (!where.contains("?")) {
// ALog.e(TAG, String.format("请在where语句的'='后编写?,参数信息:%s", Arrays.toString(expression)));
// return false;
//}
Pattern pattern = Pattern.compile("\\?");
Matcher matcher = pattern.matcher(where);
int count = 0;
while (matcher.find()) {
count++;
}
if (count < expression.length - 1) {
ALog.e(TAG, String.format("条件语句的?个数不能小于参数个数,参数信息:%s", Arrays.toString(expression)));
return false;
}
if (count > expression.length - 1) {
ALog.e(TAG, String.format("条件语句的?个数不能大于参数个数, 参数信息:%s", Arrays.toString(expression)));
return false;
}
return true;
}
/**
* 是否是快速点击500ms内快速点击无效
*
* @return true 快速点击
*/
public static boolean isFastDoubleClick() {
long time = System.currentTimeMillis();
long timeD = time - lastClickTime;
if (0 < timeD && timeD < 500) {
ALog.i(TAG, "操作太频繁了,缓一下吧~");
return true;
}
lastClickTime = time;
return false;
}
/**
* 将字符串转换为Ftp服务器默认的ISO-8859-1编码
*
* @param charSet 原字符串编码s
* @param str 需要转换的字符串
* @return 转换后的字符串
*/
public static String convertFtpChar(String charSet, String str)
throws UnsupportedEncodingException {
return new String(str.getBytes(charSet), SERVER_CHARSET);
}
/**
* 将字符串转换为Ftp服务器默认的ISO-8859-1编码
*
* @param charSet 字符串编码
* @param str 需要转换的字符串
* @return 转换后的字符串
*/
public static String convertSFtpChar(String charSet, String str)
throws UnsupportedEncodingException {
return new String(str.getBytes(), charSet);
}
/**
* 获取某包下所有类
*
* @param className 过滤的类名
* @return 类的完整名称
*/
public static List<String> getPkgClassNames(Context context, String className) {
List<String> classNameList = new ArrayList<>();
String pPath = context.getPackageCodePath();
File dir = new File(pPath).getParentFile();
String[] paths = dir.list();
if (paths == null) {
classNameList.addAll(getPkgClassName(pPath, className));
} else {
String dPath = dir.getPath();
for (String path : dir.list()) {
String fPath = dPath + "/" + path;
if (!fPath.endsWith(".apk")) {
continue;
}
classNameList.addAll(getPkgClassName(fPath, className));
}
}
return classNameList;
}
/**
* 获取指定包名下的所有类
*
* @param path dex路径
* @param filterClass 需要过滤的类
*/
public static List<String> getPkgClassName(String path, String filterClass) {
List<String> list = new ArrayList<>();
try {
File file = new File(path);
if (!file.exists()) {
ALog.w(TAG, String.format("路径【%s】下的Dex文件不存在", path));
return list;
}
DexFile df = new DexFile(path);//通过DexFile查找当前的APK中可执行文件
Enumeration<String> enumeration = df.entries();//获取df中的元素 这里包含了所有可执行的类名 该类名包含了包名+类名的方式
while (enumeration.hasMoreElements()) {
String _className = enumeration.nextElement();
if (!_className.contains(filterClass)) {
continue;
}
if (_className.contains(filterClass)) {
list.add(_className);
}
}
df.close();
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
/**
* 拦截window.location.replace数据
*
* @return 重定向url
*/
public static String getWindowReplaceUrl(String text) {
if (TextUtils.isEmpty(text)) {
ALog.e(TAG, "拦截数据为null");
return null;
}
String reg = Regular.REG_WINLOD_REPLACE;
Pattern p = Pattern.compile(reg);
Matcher m = p.matcher(text);
if (m.find()) {
String s = m.group();
s = s.substring(9, s.length() - 2);
return s;
}
return null;
}
/**
* 获取sdcard app的缓存目录
*
* @return "/mnt/sdcard/Android/data/{package_name}/files/"
*/
public static String getAppPath(Context context) {
//判断是否存在sd卡
boolean sdExist = android.os.Environment.MEDIA_MOUNTED.equals(
android.os.Environment.getExternalStorageState());
if (!sdExist) {
return null;
} else {
//获取sd卡路径
File file = context.getExternalFilesDir(null);
String dir;
if (file != null) {
dir = file.getPath() + "/";
} else {
dir = Environment.getExternalStorageDirectory().getPath()
+ "/Android/data/"
+ context.getPackageName()
+ "/files/";
}
return dir;
}
}
/**
* 获取map泛型类型
*
* @param map list类型字段
* @return 泛型类型
*/
public static Class[] getMapParamType(Field map) {
Class type = map.getType();
if (!type.isAssignableFrom(Map.class)) {
ALog.d(TAG, "字段类型不是Map");
return null;
}
Type fc = map.getGenericType();
if (fc == null) {
ALog.d(TAG, "该字段没有泛型参数");
return null;
}
if (fc instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) fc;
Type[] types = pt.getActualTypeArguments();
Class[] clazz = new Class[2];
clazz[0] = (Class) types[0];
clazz[1] = (Class) types[1];
return clazz;
}
return null;
}
/**
* 获取list泛型类型
*
* @param list list类型字段
* @return 泛型类型
*/
public static Class getListParamType(Field list) {
Class type = list.getType();
if (!type.isAssignableFrom(List.class)) {
ALog.d(TAG, "字段类型不是List");
return null;
}
Type fc = list.getGenericType(); // 关键的地方如果是List类型得到其Generic的类型
if (fc == null) {
ALog.d(TAG, "该字段没有泛型参数");
return null;
}
if (fc instanceof ParameterizedType) { //如果是泛型参数的类型
ParameterizedType pt = (ParameterizedType) fc;
return (Class) pt.getActualTypeArguments()[0]; //得到泛型里的class类型对象。
}
return null;
}
/**
* 分割获取url协议ip/域名,端口,内容
*
* @param url 输入的url{@code String url = "ftp://z:z@dygod18.com:21211/[电影天堂www.dy2018.com]猩球崛起3终极之战BD国英双语中英双字.mkv";}
*/
public static FtpUrlEntity getFtpUrlInfo(String url) {
Uri uri = Uri.parse(url);
String userInfo = uri.getUserInfo(), remotePath = uri.getPath();
ALog.d(TAG,
String.format("scheme = %s, user = %s, host = %s, port = %s, path = %s", uri.getScheme(),
userInfo, uri.getHost(), uri.getPort(), remotePath));
FtpUrlEntity entity = new FtpUrlEntity();
entity.url = url;
entity.hostName = uri.getHost();
entity.port = uri.getPort() == -1 ? "21" : String.valueOf(uri.getPort());
if (!TextUtils.isEmpty(userInfo)) {
String[] temp = userInfo.split(":");
if (temp.length == 2) {
entity.user = temp[0];
entity.password = temp[1];
} else {
entity.user = userInfo;
}
}
entity.scheme = uri.getScheme();
entity.remotePath = TextUtils.isEmpty(remotePath) ? "/" : remotePath;
return entity;
}
/**
* 转换Url
*
* @param url 原地址
* @return 转换后的地址
*/
public static String convertUrl(String url) {
Uri uri = Uri.parse(url);
url = uri.toString();
if (hasDoubleCharacter(url)) {
//预先处理空格URLEncoder只会把空格转换为+
url = url.replaceAll(" ", "%20");
//匹配双字节字符(包括汉字在内)
String regex = Regular.REG_DOUBLE_CHAR_AND_SPACE;
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(url);
Set<String> strs = new HashSet<>();
while (m.find()) {
strs.add(m.group());
}
try {
for (String str : strs) {
url = url.replaceAll(str, URLEncoder.encode(str, "UTF-8"));
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return url;
}
/**
* 判断是否有双字节字符(包括汉字在内) 和空格、制表符、回车
*
* @param chineseStr 需要进行判断的字符串
* @return {@code true}有双字节字符,{@code false} 无双字节字符
*/
public static boolean hasDoubleCharacter(String chineseStr) {
char[] charArray = chineseStr.toCharArray();
for (char aCharArray : charArray) {
if (((aCharArray >= 0x0391) && (aCharArray <= 0xFFE5)) || (aCharArray == 0x0d) || (aCharArray
== 0x0a) || (aCharArray == 0x20)) {
return true;
}
}
return false;
}
/**
* base64 解密字符串
*
* @param str 被加密的字符串
* @return 解密后的字符串
*/
public static String decryptBASE64(String str) {
return new String(Base64.decode(str.getBytes(), Base64.DEFAULT));
}
/**
* base64 加密字符串
*
* @param str 需要加密的字符串
* @return 加密后的字符串
*/
public static String encryptBASE64(String str) {
return Base64.encodeToString(str.getBytes(), Base64.DEFAULT);
}
/**
* 字符串编码转换
*/
public static String strCharSetConvert(String oldStr, String charSet) {
try {
return new String(oldStr.getBytes(), charSet);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
/**
* 根据下载任务组的url创建key
*
* @return urls 为 null 或者 size为0返回""
*/
public static String getMd5Code(List<String> urls) {
if (urls == null || urls.size() < 1) return "";
String md5 = "";
StringBuilder sb = new StringBuilder();
for (String url : urls) {
sb.append(url);
}
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(sb.toString().getBytes());
md5 = new BigInteger(1, md.digest()).toString(16);
} catch (NoSuchAlgorithmException e) {
ALog.e(TAG, e.getMessage());
}
return md5;
}
/**
* 获取字符串的md5
*
* @return 字符串为空或获取md5失败则返回""
*/
public static String getStrMd5(String str) {
if (TextUtils.isEmpty(str)) return "";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes());
return new BigInteger(1, md.digest()).toString(16);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
/**
* 获取CPU核心数
*/
public static int getCoresNum() {
//Private Class to display only CPU devices in the directory listing
class CpuFilter implements FileFilter {
@Override public boolean accept(File pathname) {
//Check if filename is "cpu", followed by a single digit number
return Pattern.matches("cpu[0-9]", pathname.getName());
}
}
try {
//Get directory containing CPU info
File dir = new File("/sys/devices/system/cpu/");
//Filter to only list the devices we care about
File[] files = dir.listFiles(new CpuFilter());
ALog.d(TAG, "CPU Count: " + files.length);
//Return the number of cores (virtual CPU devices)
return files.length;
} catch (Exception e) {
//Print exception
ALog.d(TAG, "CPU Count: Failed.");
e.printStackTrace();
//Default to return 1 core
return 1;
}
}
/**
* 校验文件MD5码
*/
public static boolean checkMD5(String md5, File updateFile) {
if (TextUtils.isEmpty(md5) || updateFile == null) {
ALog.e(TAG, "MD5 string empty or updateFile null");
return false;
}
String calculatedDigest = getFileMD5(updateFile);
if (calculatedDigest == null) {
ALog.e(TAG, "calculatedDigest null");
return false;
}
return calculatedDigest.equalsIgnoreCase(md5);
}
/**
* 校验文件MD5码
*/
public static boolean checkMD5(String md5, InputStream is) {
if (TextUtils.isEmpty(md5) || is == null) {
ALog.e(TAG, "MD5 string empty or updateFile null");
return false;
}
String calculatedDigest = getFileMD5(is);
if (calculatedDigest == null) {
ALog.e(TAG, "calculatedDigest null");
return false;
}
return calculatedDigest.equalsIgnoreCase(md5);
}
/**
* 获取文件MD5码
*/
public static String getFileMD5(File updateFile) {
InputStream is;
try {
is = new FileInputStream(updateFile);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
}
return getFileMD5(is);
}
/**
* 获取文件MD5码
*/
public static String getFileMD5(InputStream is) {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
byte[] buffer = new byte[8192];
int read;
try {
while ((read = is.read(buffer)) > 0) {
digest.update(buffer, 0, read);
}
byte[] md5sum = digest.digest();
BigInteger bigInt = new BigInteger(1, md5sum);
String output = bigInt.toString(16);
// Fill to 32 chars
output = String.format("%32s", output).replace(' ', '0');
return output;
} catch (IOException e) {
throw new RuntimeException("Unable to process file for MD5", e);
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 创建隐性的Intent
*/
public static Intent createIntent(String packageName, String action) {
Uri.Builder builder = new Uri.Builder();
builder.scheme(packageName);
Uri uri = builder.build();
Intent intent = new Intent(action);
intent.setData(uri);
return intent;
}
/**
* 存储字符串到配置文件
*
* @param preName 配置文件名
* @param key 存储的键值
* @param value 需要存储的字符串
* @return 成功标志
*/
public static Boolean putString(String preName, Context context, String key, String value) {
SharedPreferences pre = context.getSharedPreferences(preName, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = pre.edit();
editor.putString(key, value);
return editor.commit();
}
/**
* 从配置文件读取字符串
*
* @param preName 配置文件名
* @param key 字符串键值
* @return 键值对应的字符串, 默认返回""
*/
public static String getString(String preName, Context context, String key) {
SharedPreferences pre = context.getSharedPreferences(preName, Context.MODE_PRIVATE);
return pre.getString(key, "");
}
/**
* 获取所有字段,包括父类的字段
*/
public static List<Field> getAllFields(Class clazz) {
List<Field> fields = new ArrayList<>();
Class personClazz = clazz.getSuperclass();
if (personClazz != null) {
Class rootClazz = personClazz.getSuperclass();
if (rootClazz != null) {
Collections.addAll(fields, rootClazz.getDeclaredFields());
}
Collections.addAll(fields, personClazz.getDeclaredFields());
}
Collections.addAll(fields, clazz.getDeclaredFields());
List<Field> ignore = new ArrayList<>();
for (Field field : fields) {
if (field.getName().equals(AriaConfig.IGNORE_CLASS_KLASS) || field.getName()
.equals(AriaConfig.IGNORE_CLASS_MONITOR)) {
ignore.add(field);
}
}
if (!ignore.isEmpty()) {
fields.removeAll(ignore);
}
return fields;
}
/**
* 获取当前类里面的所在字段
*/
public static Field[] getFields(Class clazz) {
Field[] fields;
fields = clazz.getDeclaredFields();
if (fields.length == 0) {
Class superClazz = clazz.getSuperclass();
if (superClazz != null) {
fields = getFields(superClazz);
}
}
return fields;
}
/**
* 获取类里面的指定对象,如果该类没有则从父类查询
*/
public static Field getField(Class clazz, String name) {
Field field = null;
try {
field = clazz.getDeclaredField(name);
} catch (NoSuchFieldException e) {
try {
field = clazz.getField(name);
} catch (NoSuchFieldException e1) {
if (clazz.getSuperclass() == null) {
return field;
} else {
field = getField(clazz.getSuperclass(), name);
}
}
}
if (field != null) {
field.setAccessible(true);
}
return field;
}
/**
* 字符串转hashcode
*/
public static int keyToHashCode(String str) {
int total = 0;
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (ch == '-') ch = (char) 28; // does not contain the same last 5 bits as any letter
if (ch == '\'') ch = (char) 29; // nor this
total = (total * 33) + (ch & 0x1F);
}
return total;
}
/**
* 将key转换为16进制码
*
* @param key 缓存的key
* @return 转换后的key的值, 系统便是通过该key来读写缓存
*/
public static String keyToHashKey(String key) {
String cacheKey;
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(key.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey = String.valueOf(key.hashCode());
}
return cacheKey;
}
/**
* 将普通字符串转换为16位进制字符串
*/
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("0x");
if (src == null || src.length <= 0) {
return null;
}
char[] buffer = new char[2];
for (byte aSrc : src) {
buffer[0] = Character.forDigit((aSrc >>> 4) & 0x0F, 16);
buffer[1] = Character.forDigit(aSrc & 0x0F, 16);
stringBuilder.append(buffer);
}
return stringBuilder.toString();
}
/**
* 获取对象名
*
* @param obj 对象
* @return 对象名
*/
public static String getClassName(Object obj) {
String[] arrays = obj.getClass().getName().split("\\.");
return arrays[arrays.length - 1];
}
/**
* 获取对象名
*
* @param clazz clazz
* @return 对象名
*/
public static String getClassName(Class clazz) {
String[] arrays = clazz.getName().split("\\.");
return arrays[arrays.length - 1];
}
/**
* 格式化文件大小
*
* @param size file.length() 获取文件大小
*/
public static String formatFileSize(double size) {
if (size < 0) {
return "0kb";
}
double kiloByte = size / 1024;
if (kiloByte < 1) {
return size + "b";
}
double megaByte = kiloByte / 1024;
if (megaByte < 1) {
BigDecimal result1 = new BigDecimal(Double.toString(kiloByte));
return result1.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "kb";
}
double gigaByte = megaByte / 1024;
if (gigaByte < 1) {
BigDecimal result2 = new BigDecimal(Double.toString(megaByte));
return result2.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "mb";
}
double teraBytes = gigaByte / 1024;
if (teraBytes < 1) {
BigDecimal result3 = new BigDecimal(Double.toString(gigaByte));
return result3.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "gb";
}
BigDecimal result4 = new BigDecimal(teraBytes);
return result4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "tb";
}
/**
* 转换时间
* 时间1 小时,显示分秒,显示样式 00:20
* 时间 ≥1 小时,显示时分秒,显示样式 01:11:12
* 时间 ≥1 天,显示天时分,显示样式 1d 01:11
* 时间 ≥7 天,显示样式 ∞
*
* @param seconds 单位为s的时间
*/
public static String formatTime(int seconds) {
String standardTime;
if (seconds <= 0) {
standardTime = "00:00";
} else if (seconds < 60) {
standardTime = String.format(Locale.getDefault(), "00:%02d", seconds % 60);
} else if (seconds < 3600) {
standardTime = String.format(Locale.getDefault(), "%02d:%02d", seconds / 60, seconds % 60);
} else if (seconds < 86400) {
standardTime =
String.format(Locale.getDefault(), "%02d:%02d:%02d", seconds / 3600, seconds % 3600 / 60,
seconds % 60);
} else if (seconds < 604800) {
standardTime =
String.format(Locale.getDefault(), "%dd %02d:%02d", seconds / 86400,
seconds % 86400 / 3600,
seconds % 3600);
} else {
standardTime = "";
}
return standardTime;
}
/**
* 通过文件名获取下载配置文件路径
*
* @param fileName 文件名
*/
public static String getFileConfigPath(boolean isDownload, String fileName) {
return AriaConfig.getInstance().getAPP().getFilesDir().getPath() + (isDownload
? AriaConfig.DOWNLOAD_TEMP_DIR
: AriaConfig.UPLOAD_TEMP_DIR) + fileName + ".properties";
}
}