package com.proxgrind.chameleon.utils.tools; import android.Manifest; import android.app.Activity; import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.View; import android.widget.AbsListView; import android.widget.Button; import android.widget.TextView; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.UUID; import com.proxgrind.chameleon.R; import com.proxgrind.chameleon.activities.DevicesFastActivity; import com.proxgrind.chameleon.executor.ChameleonExecutorProxy; import com.proxgrind.chameleon.javabean.DevBean; import com.proxgrind.chameleon.javabean.DumpBean; import com.proxgrind.chameleon.utils.mifare.DumpUtils; import com.proxgrind.chameleon.utils.stream.FileUtils; import com.proxgrind.chameleon.utils.system.AppContextUtils; import com.proxgrind.chameleon.utils.system.AppListUtils; import com.proxgrind.chameleon.utils.system.AppResourceUtils; import com.proxgrind.chameleon.utils.system.LogUtils; import com.proxgrind.chameleon.widget.MaterialAlertDialog; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; /** * Created by DXL on 2017/10/27. */ public class Commons { //短称路径 public static final String LOG_TAG = Commons.class.getSimpleName(); // 子模块包名! private static final String SUB_DEBUG_PACK_NAME = "com.proxgrind.debug"; // Dump文件固定的时间格式! public static final String DEFAULT_TIME_DECRATE = "yyyy-MM-dd_HH:mm:ss"; public static SharedPreferences sp = AppContextUtils.app.getSharedPreferences("SaveMap", Context.MODE_PRIVATE); public static SharedPreferences.Editor editor = sp.edit(); private Commons() { } public static String[] getPermissionsOfAppRequired() { ArrayList perList = new ArrayList<>(); perList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); perList.add(Manifest.permission.READ_EXTERNAL_STORAGE); // perList.add(Manifest.permission.ACCESS_COARSE_LOCATION); perList.add(Manifest.permission.ACCESS_FINE_LOCATION); return perList.toArray(new String[0]); } //呼叫QQ public static void callQQ(Context context, String qq, Runnable onFaild) { //这里的228451878是自己指定的QQ号码,可以自己更换 String url = "mqqwpa://im/chat?chat_type=wpa&uin=" + qq; try { context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); } catch (Exception e) { onFaild.run(); } } //打开浏览器,链接到指定的链接 public static void openUrl(Context context, String url) { try { Uri uri = Uri.parse(url); Intent intent = new Intent(); intent.setAction("android.intent.action.VIEW"); intent.setData(uri); context.startActivity(intent); } catch (Exception e) { e.printStackTrace(); } } //获取包中的文件的URI public static Uri getUriFromResource(String packagePath, String filePath) { return Uri.parse("android.resource://" + packagePath + "/" + filePath); } //移除设备对象从集合中! public static boolean removeDevByList(DevBean devBean, List list) { if (devBean != null) { String name = devBean.getDevName(); String addr = devBean.getMacAddress(); if (name == null) return false; for (int i = 0; i < list.size(); i++) { DevBean tmpBean = list.get(i); if (tmpBean == null) return false; String n = tmpBean.getDevName(); String a = tmpBean.getMacAddress(); if (n == null) return false; if (n.equals(name) && a.equals(addr)) { list.remove(tmpBean); return true; } } } else { return false; } return true; } //该设备实体之中的字段是否是空的! public static boolean isDevBeanDataNotNull(DevBean devBean) { if (devBean == null) return false; return devBean.getMacAddress() != null; } //判断两个设备是否是一致的 public static boolean equalDebBean(DevBean a, DevBean b) { if (a == b) return true; if (a == null || b == null) return false; if (isDevBeanDataNotNull(a) && isDevBeanDataNotNull(b)) { return a.getMacAddress().equals(b.getMacAddress()); } return false; } //从蓝牙适配器中取出历史连接的设备列表! public static DevBean[] getDevsFromBTAdapter(BluetoothAdapter btAdapter) { ArrayList devList = new ArrayList<>(); Set pairedDevices = btAdapter.getBondedDevices(); if (pairedDevices == null) return null; if (pairedDevices.size() > 0) { ArrayList tmpList = new ArrayList<>(pairedDevices); for (int i = 0; i < tmpList.size(); ++i) { devList.add(new DevBean(tmpList.get(i).getName(), tmpList.get(i).getAddress())); } } else { return null; } return ArrayUtils.list2Arr(devList); } //设备是否是USB设备! public static boolean isUsbDevice(String address) { if (address == null) return false; //这三种mac是开发者定义的用于区分USB设备和蓝牙设备的特征符! switch (address) { case "00:00:00:00:00:00": case "00:00:00:00:00:01": case "00:00:00:00:00:02": return true; } return false; } // 获得相应的从值中! public static int getPositionFromValue(String str, List list) { return list.indexOf(str); } // 保存Dump到目录! public static boolean saveDump2Local(byte[] dump, String name) { File dumpDir = FileUtils.getAppFilesDir("dump"); try { FileUtils.writeBytes(dump, FileUtils.newFile(dumpDir, name), false); return true; } catch (IOException e) { e.printStackTrace(); } return false; } public static File[] listInternalDump() { File dumpDir = FileUtils.getAppFilesDir("dump"); return dumpDir.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { return pathname.getName().endsWith(".dump"); } }); } public static String getTimeDecorate() { Date date = new Date(); //获取当前的系统时间。 SimpleDateFormat dateFormat = new SimpleDateFormat(DEFAULT_TIME_DECRATE, Locale.getDefault()); //使用了默认的格式创建了一个日期格式化对象。 return dateFormat.format(date); } public static String getTimeDecorate(String fileName) { String time = RegexGroupUtils.matcherGroup( fileName, ".*\\|(.*)\\..*", 1, 0); if (time == null) return null; try { return new SimpleDateFormat(DEFAULT_TIME_DECRATE, Locale.CHINESE).format(time); } catch (Exception e) { e.printStackTrace(); } return null; } public static String getInternalDumpDir() { return FileUtils.getAppFilesDir("dump").getAbsolutePath(); } public static String getSdcardDir() { return Environment.getExternalStorageDirectory().getAbsolutePath(); } public static String getSdcardDownloadDir() { return getSdcardDir() + File.separator + "Download"; } public static String getSdcardAppDownloadDir() { String filePath = getSdcardDownloadDir() + File.separator + AppContextUtils.app.getPackageName(); FileUtils.createPaths(new File(filePath)); return filePath; } public static String getDumpName(String prefix) { return getDumpName(prefix, getTimeDecorate()); } public static String getDumpRawName(String name) { // 1A0B42C8(data.dump)|2020-02-20_15:49:06.dump return RegexGroupUtils.matcherGroup(name, ".*\\((.*)\\)\\|.*", 1, 0); } public static String getDumpName(String prefix, String centerfix) { return prefix + "|" + centerfix + ".dump"; } public static String getDumpFile(String name) { return FileUtils.getAppFilesDir("dump") + File.separator + name; } public static DumpBean[] files2DumpBeans(File[] files, boolean isDescendingOrder, boolean isNeedNameSort) { ArrayList ret = new ArrayList<>(); if (files != null) { for (File f : files) { String name = f.getName(); DumpBean info = new DumpBean(name); // 裁取UID! String uid = RegexGroupUtils.matcherGroup( name, "(.*)\\|.*", 1, 0); String time = RegexGroupUtils.matcherGroup( name, ".*\\|(.*)\\..*", 1, 0); if (uid != null && time != null) { info.setUid(uid); info.setTime(time); } else { info.setUid(f.getName()); if (time != null) { info.setTime(time); } else { info.setTime("0000-00-00_00:00:00"); } } info.setName(f.getName()); ret.add(info); } } Comparator nameComparator = new Comparator() { @Override public int compare(DumpBean o1, DumpBean o2) { if (isNeedNameSort) { String uid1 = o1.getUid(); String uid2 = o2.getUid(); if (uid1 != null && uid2 != null) { if (isDescendingOrder) { return uid2.compareTo(uid1); } else { // 升序 return uid1.compareTo(uid2); } } return 0; } // 不需要使用名称来排序则直接进行无操作状态返回! return 0; } }; Comparator dateComparator = new Comparator() { @Override public int compare(DumpBean o1, DumpBean o2) { SimpleDateFormat format = new SimpleDateFormat(DEFAULT_TIME_DECRATE, Locale.getDefault()); try { Date dt1 = format.parse(o1.getTime()); Date dt2 = format.parse(o2.getTime()); if (dt1 != null && dt2 != null) { if (isDescendingOrder) return Long.compare(dt2.getTime(), dt1.getTime()); else return Long.compare(dt1.getTime(), dt2.getTime()); } else { return 0; } } catch (Exception e) { e.printStackTrace(); } return 0; } }; List> comparatorList = new ArrayList<>(Arrays.asList(nameComparator, dateComparator)); Collections.sort(ret, new Comparator() { @Override public int compare(DumpBean o1, DumpBean o2) { // 用实际的比较器比较! for (Comparator tmpComparator : comparatorList) { if (tmpComparator.compare(o1, o2) > 0) { return 1; } else if (tmpComparator.compare(o1, o2) < 0) { return -1; } } return 0; } }); return ret.toArray(new DumpBean[0]); } public static boolean addDumpFileToInternal(String name, byte[] content) { // 取出内部的文件! DumpBean[] dumpBeans = files2DumpBeans(listInternalDump(), false, false); boolean canAdd = true; for (DumpBean bean : dumpBeans) { // 如果已经存在相同的名字的文件的话,则进行MD5检测! String rawName = getDumpRawName(bean.getName()); LogUtils.d("******************************"); LogUtils.d("名称(bean): " + rawName); LogUtils.d("名称(file): " + name); LogUtils.d("******************************"); if (name.equals(rawName)) { File dumpFile = new File(getDumpFile(bean.getName())); LogUtils.d("已经存在该文件名,将会尝试进行对比:" + dumpFile.getName()); try { byte[] dumpDigest = FileUtils.readBytes(dumpFile); String digestStr = MD5Utils.digest(dumpDigest); // 如果检测到MD5相同,则跳过添加,否则添加! if (MD5Utils.verify(digestStr, content)) { canAdd = false; break; } } catch (IOException e) { e.printStackTrace(); return false; } } } if (canAdd) { String perviousUID = "No UID."; // 截取UID! int type = DumpUtils.getType(content); if (type == DumpUtils.TYPE_TXT) { String[] datas = DumpUtils.getTxt(content); if (datas != null) { perviousUID = datas[0].substring(0, 8); } } else if (type == DumpUtils.TYPE_BIN) { perviousUID = HexUtil.toHexString(content, 0, 4); } else { return false; } // 截取完整的源文件名字! // 获得一个时间修饰! String time = Commons.getTimeDecorate(); // 拼接将要保存到内部的文件名! File target = new File(Commons.getDumpFile( perviousUID + "(" + name + ")|" + time + ".dump") ); // 直接将已经读取出来的数据字节组写入到内部文件! return FileUtils.copy(content, target); } return false; } public static int getMaxWidthOnChildren(AbsListView absListView) { if (absListView == null || absListView.getAdapter() == null) { // pre-condition return -1; } //int totalHeight = 0; int maxWidth = 0; for (int i = 0; i < absListView.getAdapter().getCount(); i++) { View listItem = absListView.getAdapter().getView(i, null, absListView); listItem.measure(0, 0); //totalHeight += listItem.getMeasuredHeight(); int width = listItem.getMeasuredWidth(); if (width > maxWidth) maxWidth = width; LogUtils.d("tmp: " + width); } return maxWidth; } // 是否快速在30秒内反复重启! public static boolean isAppFastRepeatedRestart() { File appDir = FileUtils.getAppFilesDir("config"); if (appDir != null) { int maxCount = 5; String key = "FastRestart"; File timeFile = new File(appDir.getAbsolutePath() + File.separator + key + ".config"); if (!timeFile.exists()) FileUtils.createFile(timeFile); // 判断反复重启的思路就是,存入5个时间段的时间戳,如果这五个时间段的总共距离不超过30s,则判定为快速反复重启! try { String[] values = new String[0]; if (DiskKVUtil.isKVExists(key, timeFile) && (values = DiskKVUtil.queryKVLine(key, timeFile)).length == maxCount) { // 有五次记录,我们需要看他的时间! long[] times = new long[values.length]; // 转换为时间戳! for (int i = 0; i < times.length; i++) { String tmpTimeStr0 = values[i]; times[i] = Long.valueOf(tmpTimeStr0); } // 进行排序! Arrays.sort(times); LogUtils.d(Arrays.toString(times)); // 获取最小值与当前时间对比! long timeCount = 0; for (int i = times.length - 1; i > 0; ) { LogUtils.d("双方时间戳: " + times[i] + "," + times[i - 1]); timeCount += times[i] - times[--i]; LogUtils.d("差值timeCount: " + timeCount); } System.arraycopy(times, 1, times, 0, times.length - 1); //实现左移,然后最后一个位置更新距离开机的时间,如果最后一个时间和最开始时间小于DURATION,即连续5次启动 times[times.length - 1] = System.currentTimeMillis(); // 转换为时间戳重新写入! for (int i = 0; i < values.length; i++) { values[i] = String.valueOf(times[i]); } DiskKVUtil.update2Disk(key, values, timeFile); boolean isValueFarAway = (System.currentTimeMillis() - times[0]) > ((1000 * 30)); if (timeCount < 1000 * 30 && !isValueFarAway) { // 三十秒内超五次 return true; } } else { if (values.length < maxCount) { LogUtils.d("键值对(操作次数)不到五个,将会自动插入: " + values.length); DiskKVUtil.insertKV(key, String.valueOf(System.currentTimeMillis()), timeFile); } else { DiskKVUtil.deleteKV(key, timeFile); } } } catch (IOException e) { e.printStackTrace(); } } else { LogUtils.d("配置目录不存在!"); } return false; } // 是否拥有DEBUG的权限! public static boolean isCanAccessRoot() { // 进行信息检测,查找索引! List appList = AppListUtils.getInstalledApplication(AppContextUtils.app, false); for (ResolveInfo resolveInfo : appList) { if (SUB_DEBUG_PACK_NAME.equalsIgnoreCase(resolveInfo.activityInfo.packageName)) { // 有SUB APP,可以通过放行! appList.clear(); return true; } } appList.clear(); return false; } public static void showDialogOnOfflineMode(Context context) { if (!ChameleonExecutorProxy.getInstance().isConnected()) { new AlertDialog.Builder(context) .setTitle(R.string.tips) .setMessage(R.string.tips_offline_mode) .setPositiveButton(R.string.go2, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { context.startActivity(new Intent(context, DevicesFastActivity.class)); } }) .setCancelable(false) .setNegativeButton(R.string.cancel, null).show(); } } // 当前的操作是否是在主线程中执行的! public static boolean isRunOnMainThread() { return Thread.currentThread().getId() == Looper.getMainLooper().getThread().getId(); } /** * @return 获取本地包 */ public static long getVerCode() { long verCode = -1; try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { verCode = AppContextUtils.app.getPackageManager().getPackageInfo( AppContextUtils.app.getPackageName(), 0).getLongVersionCode(); } else { verCode = AppContextUtils.app.getPackageManager().getPackageInfo( AppContextUtils.app.getPackageName(), 0).versionCode; } } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return verCode; } /** * @return 获取本地包 */ public static String getVerName() { String verCode = "NoName"; try { verCode = AppContextUtils.app.getPackageManager().getPackageInfo( AppContextUtils.app.getPackageName(), 0).versionName; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return verCode; } public static void logStackTrace() { StackTraceElement[] stack = Thread.currentThread().getStackTrace(); for (StackTraceElement stackTraceElement : stack) { Log.d("logStackTrace", stackTraceElement.getClassName() + " }:{ " + stackTraceElement.getMethodName()); } } public static void showExportSuccessDialog(@Nullable Activity activity, Uri path) { if (activity == null) return; activity.runOnUiThread(new Runnable() { @Override public void run() { // 当前的目标使用的是外部路径! new AlertDialog.Builder(activity) .setTitle(R.string.tips) .setCancelable(false) .setMessage(activity.getString(R.string.tips_dump_export_success) + ": " + FileUtils.getFilePathByUri(path)) .setPositiveButton(R.string.ok, null) .setNegativeButton(R.string.share, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { FileUtils.shareFile(activity, path); } }) .show(); } }); } // 保存文件到临时目录! public static String saveFileTemp(Context context, Uri uri) { String internalPath = FileUtils.getAppFilesDir("temp").getPath(); // 截取完整的源文件名字! String sourceName = FileUtils.getFileNameByUri(context, uri, false); if (sourceName == null) { String tempPath = FileUtils.getFilePathByUri(uri); if (tempPath != null) { sourceName = new File(tempPath).getName(); } } String targetPath = internalPath + File.separator + // 尝试拼接源文件名 (sourceName == null ? UUID.randomUUID() : sourceName); try { byte[] data = FileUtils.readBytes(uri); File targetFile = new File(targetPath); FileUtils.createFile(targetFile); FileUtils.writeBytes(data, Uri.fromFile(targetFile)); } catch (IOException e) { e.printStackTrace(); return null; } return targetPath; } public static void showLowBatteryDialog(Context context, int percent) { if (percent > 0 && percent < 10) { Runnable runnable = new Runnable() { @Override public void run() { new MaterialAlertDialog.Builder(context) .setTitle(R.string.warning) .setMessage(context.getString(R.string.tips_battery_low) + " : " + percent + "%") .setStyle(new MaterialAlertDialog.OnWidgetStyle() { @Override public void onStyle(TextView title, TextView msg, Button btn1, Button btn2) { title.setTextColor(AppResourceUtils.getColor(R.color.colorTextError)); } }).show(); } }; if (isRunOnMainThread()) { runnable.run(); } else { new Handler(Looper.getMainLooper()).post(runnable); } } } // 存放MAP public static void setMap(String key, LinkedHashMap datas) { JSONArray mJsonArray = new JSONArray(); Iterator> iterator = datas.entrySet().iterator(); JSONObject object = new JSONObject(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); try { object.put(entry.getKey(), entry.getValue()); } catch (JSONException e) { } } mJsonArray.put(object); editor.putString(key, mJsonArray.toString()); editor.commit(); } // 取出MAP public static LinkedHashMap getMap(String key) { LinkedHashMap datas = new LinkedHashMap<>(); String result = sp.getString(key, ""); try { JSONArray array = new JSONArray(result); for (int i = 0; i < array.length(); i++) { JSONObject itemObject = array.getJSONObject(i); JSONArray names = itemObject.names(); if (names != null) { for (int j = 0; j < names.length(); j++) { String name = names.getString(j); String value = itemObject.getString(name); datas.put(name, value); } } } } catch (JSONException e) { e.printStackTrace(); } return datas; } public static boolean isSelfBackground(Context context) { ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List appProcesses; if (activityManager != null) { appProcesses = activityManager.getRunningAppProcesses(); for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) { if (appProcess.processName.equals(context.getPackageName())) { if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { Log.i("后台", appProcess.processName); return true; } else { Log.i("前台", appProcess.processName); return false; } } } } return false; } public static int getAutoDisconnectTime() { return sp.getInt(Properties.APP_AUTO_CLOSE_TIME_KEY, 300); } public static void setAutoDisconnectTime(int time) { editor.putInt(Properties.APP_AUTO_CLOSE_TIME_KEY, time).apply(); } public static boolean isAutoDisconnect() { return sp.getBoolean(Properties.APP_AUTO_CLOSE_KEY, true); } public static void setAutoDisconnect(boolean enable) { editor.putBoolean(Properties.APP_AUTO_CLOSE_KEY, enable).apply(); } /** * 获取当前的UI模式, * 0 为跟随系统 * 1 为暗黑模式 * 2 为明亮模式 */ public static int getUIMode() { return sp.getInt(Properties.APP_UI_MODE, 0); } /** * 设置当前的UI模式, * 0 为跟随系统 * 1 为暗黑模式 * 2 为明亮模式 * * @param mode 新的模式 */ public static void setUIMode(int mode) { editor.putInt(Properties.APP_UI_MODE, mode).apply(); } /** * 获取当前的Dump模式 * 0 bin模式 * 1 hex模式 */ public static int getDumpMode() { return sp.getInt(Properties.APP_DUMP_MODE, 0); } /** * 设置当前的Dump模式 * 0 bin模式 * 1 hex模式 */ public static void setDumpMode(int mode) { editor.putInt(Properties.APP_DUMP_MODE, mode).apply(); } }